diff --git a/docs/Metadata.md b/docs/Metadata.md index fc7ed8e..85e0bf8 100644 --- a/docs/Metadata.md +++ b/docs/Metadata.md @@ -479,6 +479,12 @@ Beside the already explained `name`, `type` and `description` attributes there a A fixed value can only be edited when a new configuration entry of an multiple parameter group is created. Once this value is saved, it cannot be changed. You have to delete the entry and create a new one, if you want to change this value. +- `copyable` + + > **NOTE:** Requires free@home app version > 2.4.0 + + Adds the possibility to copy the current value into the clipboard, e.g. the addon sends some kind of ID via state to the UI and the user needs to copy that value to be able to use it elsewhere. + - `dependsOn` > **NOTE:** Requires free@home app version >= 2.4.0 @@ -620,6 +626,30 @@ Beside the already explained `name`, `type` and `description` attributes there a The addons receives the rpc everytime any other parameter of the same groups changes. The parameters of the rpc contains all parameter values of the current group and in addition to that `"parameter": "curve"`, `"group": "configs"` and whats configured in `rpcAdditionalParameters`. With all these values the addon can generate an SVG-Chart and send it as response. That chart will be shown to the user and he gets live feedback to every configuration changed by seeing an updated curve. +- `multiple` + + > **NOTE:** Requires free@home app version >= 3.5.0 + + > **NOTE:** Currently only implemented for parameters of type ``channel`` + + If this is added with a `true` value this parameter allows more that one value. + +- `minValues` + + > **NOTE:** Requires free@home app version >= 3.5.0 + + > **NOTE:** only for parameters with ``multiple: true``! + + Defines a minimum number of values (default: 0) + +- `minValues` + + > **NOTE:** Requires free@home app version >= 3.5.0 + + > **NOTE:** only for parameters with ``multiple: true``! + + Defines a maximum number of values (default: unlimited) + #### Parameter groups If an Addon provides many parameters, grouping them may be useful. @@ -919,3 +949,41 @@ This step has the `parameterGroup` property, which means it will create / edit a This step will use the `items` from that group to generate the form elements in the UI. Some of those are prefilled by values from the first step. The wizards itself do not store anything when closed, so you can create / edit multiple settings with wizards and have to save your changes at the end. + +### Limits + +> **NOTE:** Requires free@home app version >= 3.5.0 + +You can define global limits for configurable values, e.g. if you have a parameter of type "channel" that allows the selection of multiple values and that parameter is maybe part of a group that also +allows to create multiple entries you can defined a limit of the total sum of selected channels either +for that parameter group of globally for the whole addon configuration. + +```json +"limits": { + "maxChannels": { + "max": 64, + "type": "channel", + "group": "connections", + "message": ["You can select %1 more channel", "You can select %1 more channels"], + "message@de": ["Sie können %1 weiteren Kanal hinzufügen", "Sie können %1 weitere Kanäle hinzufügen"], + "fullMessage": [ + "You cannot add more channels, limit of %1 channel has been reached", + "You cannot add more channels, limit of %1 channels has been reached" + ], + "fullMessage@de": [ + "Sie können keinen weiteren Kanal mehr hinzufügen, das Limit von %1 Kanal wurde erreicht.", + "Sie können keinen weiteren Kanal mehr hinzufügen, das Limit von %1 Kanälen wurde erreicht." + ] + } +} +``` + +In this example all selected values of type "channel" in the parameter group "connections" are counted. +If the counter is under the defined limit of 64 the "message" will shown and informs the user how many +channels he can still add. If the limit is reached the "fullMessage" is shown instead as a warning and the user cannot select more channels. + +The translated string are a little bit different from the translations in other parts of the settings because they allow a numeric placeholder and therefore support plurals. +In the "message" the ``%1`` will be replaced with the remaining number of channels that can be selected until the limit is reached. In "fullMessage" the placeholder ``%1`` will be replaced with the value of ``max``. + +For plural support the translated string are an array of up to 3 values. Depending of the number that replaces the placeholder one of the values in the array is chosen as the currently translated string. +The rules that decide which value of the array is chosen can be seen here: https://doc.qt.io/qt-6/i18n-plural-rules.html \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 13a584b..a823aca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@busch-jaeger/free-at-home", - "version": "0.35.0", + "version": "0.36.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@busch-jaeger/free-at-home", - "version": "0.35.0", + "version": "0.36.0", "license": "ISC", "dependencies": { "@types/ws": "^8.5.7", diff --git a/package.json b/package.json index d20ceea..95288cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@busch-jaeger/free-at-home", - "version": "0.35.0", + "version": "0.36.0", "description": "Library for free@home local device api", "repository": { "type": "git", diff --git a/specs/addon.yaml b/specs/addon.yaml index 5255b7c..8f8b302 100644 --- a/specs/addon.yaml +++ b/specs/addon.yaml @@ -609,6 +609,24 @@ components: items: type: string maxLength: 256 + limits: + allOf: + - $ref: "#/components/schemas/TranslatedMessage" + - $ref: "#/components/schemas/TranslatedFullMessage" + type: object + additionalProperties: + type: object + required: + - max + - type + properties: + max: + type: integer + minimum: 1 + type: + type: string + group: + type: string errors: type: object additionalProperties: @@ -652,6 +670,10 @@ components: type: string multiple: type: boolean + canBeDisabled: + type: boolean + description: adds a checkbox to the displayed item (display configuration is needed for this) where the user can disable that entry. + The addon will receive an additional boolean property named "$disabled" for this entry and has to handle it accordingly. debug: type: boolean description: Show this parameter group only when debugging is enabled @@ -1003,6 +1025,9 @@ components: fixed: type: boolean description: A fixed value can only be edited when a new configuration entry of an multiple parameter group is created. Once this value is saved, it cannot be changed. + copyable: + type: boolean + description: Adds the possibility to copy the current value into the clipboard, e.g. the addon sends some kind of ID via state to the UI and the user needs to copy that value to be able to use it elsewhere. confirm: type: string description: "[Only for type=button] Show a confirm dialog with this text before sending the button event" @@ -1056,8 +1081,15 @@ components: - $ref: "#/components/schemas/BasicParameter" - type: object properties: - multiSelect: + multiple: type: boolean + description: "allow more than one channel" + minValues: + type: number + description: "Minimum values that must be selected (default: 0)" + maxValues: + type: number + description: "Maximum values that can be selected (default: unlimited)" filters: type: array items: @@ -1509,6 +1541,230 @@ components: confirm@tr: type: string + TranslatedMessage: + type: object + properties: + message: + oneOf: + - type: string + - type: array + items: + type: string + message@en: + oneOf: + - type: string + - type: array + items: + type: string + message@es: + oneOf: + - type: string + - type: array + items: + type: string + message@fr: + oneOf: + - type: string + - type: array + items: + type: string + message@it: + oneOf: + - type: string + - type: array + items: + type: string + message@nl: + oneOf: + - type: string + - type: array + items: + type: string + message@de: + oneOf: + - type: string + - type: array + items: + type: string + message@zh: + oneOf: + - type: string + - type: array + items: + type: string + message@da: + oneOf: + - type: string + - type: array + items: + type: string + message@fi: + oneOf: + - type: string + - type: array + items: + type: string + message@nb: + oneOf: + - type: string + - type: array + items: + type: string + message@pl: + oneOf: + - type: string + - type: array + items: + type: string + message@pt: + oneOf: + - type: string + - type: array + items: + type: string + message@ru: + oneOf: + - type: string + - type: array + items: + type: string + message@sv: + oneOf: + - type: string + - type: array + items: + type: string + message@el: + oneOf: + - type: string + - type: array + items: + type: string + message@cs: + oneOf: + - type: string + - type: array + items: + type: string + message@tr: + oneOf: + - type: string + - type: array + items: + type: string + + TranslatedFullMessage: + type: object + properties: + fullMessage: + oneOf: + - type: string + - type: array + items: + type: string + fullMessage@en: + oneOf: + - type: string + - type: array + items: + type: string + fullMessage@es: + oneOf: + - type: string + - type: array + items: + type: string + fullMessage@fr: + oneOf: + - type: string + - type: array + items: + type: string + fullMessage@it: + oneOf: + - type: string + - type: array + items: + type: string + fullMessage@nl: + oneOf: + - type: string + - type: array + items: + type: string + fullMessage@de: + oneOf: + - type: string + - type: array + items: + type: string + fullMessage@zh: + oneOf: + - type: string + - type: array + items: + type: string + fullMessage@da: + oneOf: + - type: string + - type: array + items: + type: string + fullMessage@fi: + oneOf: + - type: string + - type: array + items: + type: string + fullMessage@nb: + oneOf: + - type: string + - type: array + items: + type: string + fullMessage@pl: + oneOf: + - type: string + - type: array + items: + type: string + fullMessage@pt: + oneOf: + - type: string + - type: array + items: + type: string + fullMessage@ru: + oneOf: + - type: string + - type: array + items: + type: string + fullMessage@sv: + oneOf: + - type: string + - type: array + items: + type: string + fullMessage@el: + oneOf: + - type: string + - type: array + items: + type: string + fullMessage@cs: + oneOf: + - type: string + - type: array + items: + type: string + fullMessage@tr: + oneOf: + - type: string + - type: array + items: + type: string + Uri: type: string format: uri @@ -1558,6 +1814,8 @@ components: ConfigurationEntry: type: object + required: + - "items" properties: items: type: object diff --git a/src/addon.ts b/src/addon.ts index 1048a95..d17c9bb 100644 --- a/src/addon.ts +++ b/src/addon.ts @@ -117,11 +117,11 @@ export class AddOn< }; this.configurationEventSource.onmessage = (event: MessageEvent) => { try { - const data = JSON.parse(event.data); + const data = event.data ? JSON.parse(event.data) : ""; this.emit('configurationChanged', data); } catch (error) { - console.log(event.data); + console.error(`error parsing configuration data: '${event.data}'`, error); } }; } @@ -146,11 +146,11 @@ export class AddOn< }; this.applicationStateEventSource.onmessage = (event: MessageEvent) => { try { - const data = JSON.parse(event.data); + const data = event.data ? JSON.parse(event.data) : ""; this.emit('applicationStateChanged', data); } catch (error) { - console.log(event.data); + console.error(`error parsing application state: '${event.data}'`, error); } }; } @@ -179,7 +179,7 @@ export class AddOn< this.emit("event", data) } catch (error) { - console.log(event.data); + console.error(`error parsing event data: '${event.data}'`, error); } }; } diff --git a/src/addon/index.ts b/src/addon/index.ts index 7dbf28b..7903198 100644 --- a/src/addon/index.ts +++ b/src/addon/index.ts @@ -52,6 +52,8 @@ export type { TranslatedButton } from './models/TranslatedButton'; export type { TranslatedConfirm } from './models/TranslatedConfirm'; export type { TranslatedDescription } from './models/TranslatedDescription'; export type { TranslatedError } from './models/TranslatedError'; +export type { TranslatedFullMessage } from './models/TranslatedFullMessage'; +export type { TranslatedMessage } from './models/TranslatedMessage'; export type { TranslatedName } from './models/TranslatedName'; export type { TranslatedString } from './models/TranslatedString'; export type { TranslatedSubtitle } from './models/TranslatedSubtitle'; diff --git a/src/addon/models/BasicParameter.ts b/src/addon/models/BasicParameter.ts index 5289dd7..c6eeae1 100644 --- a/src/addon/models/BasicParameter.ts +++ b/src/addon/models/BasicParameter.ts @@ -49,6 +49,10 @@ export type BasicParameter = (TranslatedName & TranslatedDescription & Translate * A fixed value can only be edited when a new configuration entry of an multiple parameter group is created. Once this value is saved, it cannot be changed. */ fixed?: boolean; + /** + * Adds the possibility to copy the current value into the clipboard, e.g. the addon sends some kind of ID via state to the UI and the user needs to copy that value to be able to use it elsewhere. + */ + copyable?: boolean; /** * [Only for type=button] Show a confirm dialog with this text before sending the button event */ diff --git a/src/addon/models/ChannelParameter.ts b/src/addon/models/ChannelParameter.ts index ec87b7e..5e44186 100644 --- a/src/addon/models/ChannelParameter.ts +++ b/src/addon/models/ChannelParameter.ts @@ -8,7 +8,18 @@ import type { ChannelDatapoints } from './ChannelDatapoints'; import type { ChannelFunctionGroup } from './ChannelFunctionGroup'; export type ChannelParameter = (BasicParameter & { - multiSelect?: boolean; + /** + * allow more than one channel + */ + multiple?: boolean; + /** + * Minimum values that must be selected (default: 0) + */ + minValues?: number; + /** + * Maximum values that can be selected (default: unlimited) + */ + maxValues?: number; filters?: Array<(ChannelFunctionGroup | ChannelDatapoints)>; }); diff --git a/src/addon/models/ConfigurationEntry.ts b/src/addon/models/ConfigurationEntry.ts index 0eca712..6caeeac 100644 --- a/src/addon/models/ConfigurationEntry.ts +++ b/src/addon/models/ConfigurationEntry.ts @@ -4,7 +4,7 @@ /* eslint-disable */ export type ConfigurationEntry = { - items?: Record; + items: Record; deletable?: boolean; }; diff --git a/src/addon/models/Metadata.ts b/src/addon/models/Metadata.ts index 830eb1a..7b07132 100644 --- a/src/addon/models/Metadata.ts +++ b/src/addon/models/Metadata.ts @@ -6,6 +6,8 @@ import type { Message } from './Message'; import type { Parameters } from './Parameters'; import type { ParameterType } from './ParameterType'; +import type { TranslatedFullMessage } from './TranslatedFullMessage'; +import type { TranslatedMessage } from './TranslatedMessage'; import type { TranslatedString } from './TranslatedString'; import type { TranslatedUri } from './TranslatedUri'; import type { Wizards } from './Wizards'; @@ -35,6 +37,7 @@ export type Metadata = { minAuxFileUploadIntervalMinutes?: number; organizationId?: string; rpc?: Array; + limits?: (TranslatedMessage & TranslatedFullMessage); errors?: Record; messages?: Record; }; diff --git a/src/addon/models/ParameterGroup.ts b/src/addon/models/ParameterGroup.ts index 621149b..99735e1 100644 --- a/src/addon/models/ParameterGroup.ts +++ b/src/addon/models/ParameterGroup.ts @@ -13,6 +13,10 @@ import type { TranslatedTitle } from './TranslatedTitle'; export type ParameterGroup = (TranslatedName & { name: string; multiple?: boolean; + /** + * adds a checkbox to the displayed item (display configuration is needed for this) where the user can disable that entry. The addon will receive an additional boolean property named "$disabled" for this entry and has to handle it accordingly. + */ + canBeDisabled?: boolean; /** * Show this parameter group only when debugging is enabled */ diff --git a/src/addon/models/TranslatedFullMessage.ts b/src/addon/models/TranslatedFullMessage.ts new file mode 100644 index 0000000..7ddba68 --- /dev/null +++ b/src/addon/models/TranslatedFullMessage.ts @@ -0,0 +1,26 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type TranslatedFullMessage = { + fullMessage?: (string | Array); + 'fullMessage@en'?: (string | Array); + 'fullMessage@es'?: (string | Array); + 'fullMessage@fr'?: (string | Array); + 'fullMessage@it'?: (string | Array); + 'fullMessage@nl'?: (string | Array); + 'fullMessage@de'?: (string | Array); + 'fullMessage@zh'?: (string | Array); + 'fullMessage@da'?: (string | Array); + 'fullMessage@fi'?: (string | Array); + 'fullMessage@nb'?: (string | Array); + 'fullMessage@pl'?: (string | Array); + 'fullMessage@pt'?: (string | Array); + 'fullMessage@ru'?: (string | Array); + 'fullMessage@sv'?: (string | Array); + 'fullMessage@el'?: (string | Array); + 'fullMessage@cs'?: (string | Array); + 'fullMessage@tr'?: (string | Array); +}; + diff --git a/src/addon/models/TranslatedMessage.ts b/src/addon/models/TranslatedMessage.ts new file mode 100644 index 0000000..3c08fe3 --- /dev/null +++ b/src/addon/models/TranslatedMessage.ts @@ -0,0 +1,26 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type TranslatedMessage = { + message?: (string | Array); + 'message@en'?: (string | Array); + 'message@es'?: (string | Array); + 'message@fr'?: (string | Array); + 'message@it'?: (string | Array); + 'message@nl'?: (string | Array); + 'message@de'?: (string | Array); + 'message@zh'?: (string | Array); + 'message@da'?: (string | Array); + 'message@fi'?: (string | Array); + 'message@nb'?: (string | Array); + 'message@pl'?: (string | Array); + 'message@pt'?: (string | Array); + 'message@ru'?: (string | Array); + 'message@sv'?: (string | Array); + 'message@el'?: (string | Array); + 'message@cs'?: (string | Array); + 'message@tr'?: (string | Array); +}; +