From 64b006d01727a4dee6d55bcfa9040421113c2a27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Br=C3=A4utigam?= Date: Mon, 11 Dec 2023 13:05:43 +0100 Subject: [PATCH 01/15] update spec --- specs/addon.yaml | 4 ++++ src/addon/models/BasicParameter.ts | 2 +- src/addon/models/ConfigurationEntry.ts | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/specs/addon.yaml b/specs/addon.yaml index 4c68673..eb2c9e9 100644 --- a/specs/addon.yaml +++ b/specs/addon.yaml @@ -1381,10 +1381,14 @@ components: ConfigurationEntry: type: object + required: + - "items" properties: items: type: object additionalProperties: true + deletable: + type: boolean Configuration: type: object diff --git a/src/addon/models/BasicParameter.ts b/src/addon/models/BasicParameter.ts index c3d4583..f8b4850 100644 --- a/src/addon/models/BasicParameter.ts +++ b/src/addon/models/BasicParameter.ts @@ -14,7 +14,7 @@ export type BasicParameter = (TranslatedName & TranslatedDescription & { default?: (string | number | boolean); visible?: boolean; dependsOn?: string; - rpc?: string; + rpc?: 'getParameterValue' | 'getParameterConfig'; rpcCallOn?: 'initial' | 'everyChange'; dependsOnValues?: Array<(string | number | boolean)>; dependsOnConfig?: Array; diff --git a/src/addon/models/ConfigurationEntry.ts b/src/addon/models/ConfigurationEntry.ts index b22e633..6caeeac 100644 --- a/src/addon/models/ConfigurationEntry.ts +++ b/src/addon/models/ConfigurationEntry.ts @@ -4,6 +4,7 @@ /* eslint-disable */ export type ConfigurationEntry = { - items?: Record; + items: Record; + deletable?: boolean; }; From a26f1459b2894f36fe974aa69775f7a0f52f6901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Br=C3=A4utigam?= Date: Mon, 11 Dec 2023 13:05:57 +0100 Subject: [PATCH 02/15] add more rgb channel features --- src/api/apiChannel.ts | 2 + src/freeAtHome.ts | 6 +++ src/virtualChannels/rgbChannel.ts | 68 +++++++++++++++++++++++++++---- 3 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/api/apiChannel.ts b/src/api/apiChannel.ts index 91648b4..564fe7e 100644 --- a/src/api/apiChannel.ts +++ b/src/api/apiChannel.ts @@ -21,6 +21,7 @@ type OutputDatapointCallback = (value: string) => void; export class ApiChannel extends (EventEmitter as { new(): ChannelEventEmitter; }) { device: ApiDevice; channelNumber: number; + serialNumber: string; inputPairingToPosition: Map = new Map(); inputPositionToPairing: Map = new Map(); @@ -40,6 +41,7 @@ export class ApiChannel extends (EventEmitter as { new(): ChannelEventEmitter; } super(); this.device = device; this.channelNumber = channelNumber; + this.serialNumber = `${device.serialNumber}/ch${channelNumber.toString().padStart(4, '0')}`; if (apiChannel.floor !== undefined) this.floor = parseInt(apiChannel.floor, 16); diff --git a/src/freeAtHome.ts b/src/freeAtHome.ts index 92c22cd..b38cf1c 100644 --- a/src/freeAtHome.ts +++ b/src/freeAtHome.ts @@ -495,6 +495,12 @@ export class FreeAtHome extends (EventEmitter as { new(): Emitter }) { return new RGBChannel(channel); } + async createRGBWDevice(nativeId: string, name: string): Promise { + const device = await this.freeAtHomeApi.createDevice("RGBW", nativeId, name); + const channel = device.getChannels().next().value; + return new RGBChannel(channel); + } + public async markAllDevicesAsUnresponsive(): Promise { const promises = [] as PromiseLike[]; this.freeAtHomeApi.virtualDevicesBySerial.forEach((device: ApiVirtualDevice) => { diff --git a/src/virtualChannels/rgbChannel.ts b/src/virtualChannels/rgbChannel.ts index 520524f..47d1885 100644 --- a/src/virtualChannels/rgbChannel.ts +++ b/src/virtualChannels/rgbChannel.ts @@ -9,10 +9,21 @@ import { Datapoint } from '..'; import { uint32ToHSV, hsvTouint32, hsvToRgb, rgbToHsv } from './utilities/colorSpaceConversion'; +export type ColorMode = "hsv" | "ct" | "dim"; + interface ChannelEvents { isOnChanged(value: boolean): void; isForcedChanged(value: boolean): void; hsvChanged(h: number, s: number, v: number): void; + colorModeChanged(mode: ColorMode): void; + /** + * @param ct {number} color temperature in percent + */ + colorTemperatureChanged(ct: number): void; + /** + * @param brightness {number} brightness in percent + */ + brightnessChanged(brightness: number): void; } type ChannelEmitter = StrictEventEmitter; @@ -40,6 +51,18 @@ export class RGBChannel extends Mixin(Channel, (EventEmitter as { new(): Channel this.setDatapoint(PairingIds.AL_INFO_RGB, ((r << 16) + (g << 8) + (b << 0)).toString()); } + setColorMode(mode: ColorMode) { + this.setDatapoint(PairingIds.AL_INFO_COLOR_MODE, mode); + } + + setColorTemperature(ct: number) { + this.setDatapoint(PairingIds.AL_INFO_COLOR_TEMPERATURE, ct.toFixed(0)); + } + + setBrightness(brightness: number) { + this.setDatapoint(PairingIds.AL_INFO_ACTUAL_DIMMING_VALUE, brightness.toFixed(0)); + } + protected dataPointChanged(id: PairingIds, value: string): void { switch (id) { case PairingIds.AL_SWITCH_ON_OFF: { @@ -60,17 +83,44 @@ export class RGBChannel extends Mixin(Channel, (EventEmitter as { new(): Channel break; } - case PairingIds.AL_HSV: - { - const intValue = parseInt(value); - const [h, s, v] = uint32ToHSV(intValue); - this.emit("hsvChanged", h, s, v); - if (this.isAutoConfirm) - this.setHSV(h, s, v); - break; - } + case PairingIds.AL_HSV: { + const intValue = parseInt(value); + const [h, s, v] = uint32ToHSV(intValue); + this.emit("hsvChanged", h, s, v); + if (this.isAutoConfirm) + this.setHSV(h, s, v); + break; + } case PairingIds.AL_NIGHT: { + break; + } + + case PairingIds.AL_COLOR_MODE: { + const mode = value as ColorMode; + this.emit("colorModeChanged", mode); + if (this.isAutoConfirm) { + this.setColorMode(mode); + } + break; + } + + case PairingIds.AL_COLOR_TEMPERATURE: { + const ct = parseInt(value); + this.emit("colorTemperatureChanged", ct); + if (this.isAutoConfirm) { + this.setColorTemperature(ct); + } + break; + } + + case PairingIds.AL_ABSOLUTE_SET_VALUE_CONTROL: { + const brightness = parseInt(value); + this.emit("brightnessChanged", brightness); + if (this.isAutoConfirm) { + this.setBrightness(brightness); + } + break; } } From 5121e7a15130bc6e29af0fdc41f86cf400a0b9b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Br=C3=A4utigam?= Date: Wed, 13 Dec 2023 09:15:26 +0100 Subject: [PATCH 03/15] add missing property and documentation --- docs/Metadata.md | 56 ++++++++++++++++++++++++++++++ specs/addon.yaml | 3 ++ src/addon/models/BasicParameter.ts | 1 + 3 files changed, 60 insertions(+) diff --git a/docs/Metadata.md b/docs/Metadata.md index 2342be8..13a38d9 100644 --- a/docs/Metadata.md +++ b/docs/Metadata.md @@ -343,6 +343,18 @@ Possible types are: Shows the value as QR-Code. +- `svg` + + > **NOTE:** Requires free@home app version >= 2.4.0 + + Shows the value as SVG-Image in the UI. + +- `uuid` + + > **NOTE:** Requires free@home app version >= 2.4.0 + + Only useful for parameter groups with `multiple: true`. The UI generates a UUID when a new entry is created. This UUID can be used to identify the entry. + #### Additional parameter attributes Beside the already explained `name`, `type` and `description` attributes there are some special attributes that can be optionally added to a parameter definition: @@ -462,6 +474,50 @@ Beside the already explained `name`, `type` and `description` attributes there a If this is added with a `true` value this parameter is only shown, when the app is in debug mode. +- `rpc`, `rpcCallOn`, `rpcAdditionalParameters` + + > **NOTE:** Requires free@home app version >= 2.4.0 + + Allows retrieving the value or configuration changes for this parameter from the addon via one of two RPCs: `getParameterValue`, `getParameterConfig`. + + With `rpcCallOn` you can define when this rpc is send: + + `rpcCallOn: initial`: sends it only once when the UI opens the addon settings. + + `rpcCallOn: everyChange`: sends it every time any of the other parameter values in the same group has been changed by the user. + + `rpcAdditionalParameters`: is just an optional set of values that are added to the rpc. + + Full example: If you have a parameter group that configures something that can be explained best with a graph (e.g. a dimming curve or a color temperature calculation that changes over the day) you can use the `svg` parameter type together with these rpc settings to show the user a graph based on your current parameter settings. + + ```json + "configs": { + "name": "Configuration", + "name@de": "Konfiguration", + "multiple": true, + "display": { + "title": "$name" + }, + "items": { + ... + "curve": { + "name": "Color temperature curve today", + "name@de": "Farbtemperaturkurve heute", + "type": "svg", + "rpc": "getParameterValue", + "rpcCallOn": "everyChange", + "rpcAdditionalParameters": { + "width": 600, + "height": 300 + } + } + } + } + ``` + + 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. + #### Parameter groups If an Addon provides many parameters, grouping them may be useful. diff --git a/specs/addon.yaml b/specs/addon.yaml index eb2c9e9..1cb3090 100644 --- a/specs/addon.yaml +++ b/specs/addon.yaml @@ -900,6 +900,9 @@ components: enum: - "initial" - "everyChange" + rpcAdditionalParameters: + type: object + additionalProperties: true dependsOnValues: type: array items: diff --git a/src/addon/models/BasicParameter.ts b/src/addon/models/BasicParameter.ts index f8b4850..a609e8a 100644 --- a/src/addon/models/BasicParameter.ts +++ b/src/addon/models/BasicParameter.ts @@ -16,6 +16,7 @@ export type BasicParameter = (TranslatedName & TranslatedDescription & { dependsOn?: string; rpc?: 'getParameterValue' | 'getParameterConfig'; rpcCallOn?: 'initial' | 'everyChange'; + rpcAdditionalParameters?: Record; dependsOnValues?: Array<(string | number | boolean)>; dependsOnConfig?: Array; }); From 3dcaf38730e122202be9545d8372b75ab1ac3aab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Br=C3=A4utigam?= Date: Wed, 13 Dec 2023 09:24:40 +0100 Subject: [PATCH 04/15] 0.33.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c123f55..4943ae3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@busch-jaeger/free-at-home", - "version": "0.32.1", + "version": "0.33.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@busch-jaeger/free-at-home", - "version": "0.32.1", + "version": "0.33.0", "license": "ISC", "dependencies": { "@types/ws": "^8.5.7", diff --git a/package.json b/package.json index 9ffa7d1..6ce34c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@busch-jaeger/free-at-home", - "version": "0.32.1", + "version": "0.33.0", "description": "Library for free@home local device api", "repository": { "type": "git", From c029a20026e580e74241f7a22f8aff56bf476304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Br=C3=A4utigam?= Date: Thu, 14 Dec 2023 13:44:00 +0100 Subject: [PATCH 05/15] socket paths do not path api paths --- src/fhapi/core/request.ts | 3 +-- src/rpc/core/request.ts | 3 +-- src/scripting/core/request.ts | 26 +++++++++++++++----------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/fhapi/core/request.ts b/src/fhapi/core/request.ts index b0aeaea..6629a04 100644 --- a/src/fhapi/core/request.ts +++ b/src/fhapi/core/request.ts @@ -213,8 +213,7 @@ const socketAgents = new Map(); function getAgent(url: string) { if (useUnixSocket) { try { - const parsedUrl = new URL(url); - const apiPath = parsedUrl.pathname; + const apiPath = "/api/fhapi/v1"; if (!socketAgents.has(apiPath)) { console.log('creating unix socket agent for ', apiPath); const unixSocketAgent = new http.Agent({ diff --git a/src/rpc/core/request.ts b/src/rpc/core/request.ts index b0aeaea..321c393 100644 --- a/src/rpc/core/request.ts +++ b/src/rpc/core/request.ts @@ -213,8 +213,7 @@ const socketAgents = new Map(); function getAgent(url: string) { if (useUnixSocket) { try { - const parsedUrl = new URL(url); - const apiPath = parsedUrl.pathname; + const apiPath = "/api/rpc/v1"; if (!socketAgents.has(apiPath)) { console.log('creating unix socket agent for ', apiPath); const unixSocketAgent = new http.Agent({ diff --git a/src/scripting/core/request.ts b/src/scripting/core/request.ts index 503f0ad..20ff64a 100644 --- a/src/scripting/core/request.ts +++ b/src/scripting/core/request.ts @@ -211,17 +211,21 @@ const tcpSocketAgent = new http.Agent({ const socketAgents = new Map(); function getAgent(url: string) { - if (useUnixSocket && process.env.FREEATHOME_BASE_URL && url.startsWith(process.env.FREEATHOME_BASE_URL)) { - const apiPath = url.substring(process.env.FREEATHOME_BASE_URL.length); - if (!socketAgents.has(apiPath)) { - console.log('creating unix socket agent for ', apiPath); - const unixSocketAgent = new http.Agent({ - socketPath: "/run" + apiPath, - }); - socketAgents.set(apiPath, unixSocketAgent); - return unixSocketAgent; - } - return socketAgents.get(apiPath); + if (useUnixSocket) { + try { + const apiPath = "/api/scripting/v1"; + if (!socketAgents.has(apiPath)) { + console.log('creating unix socket agent for', apiPath); + const unixSocketAgent = new http.Agent({ + socketPath: "/run" + apiPath, + }); + socketAgents.set(apiPath, unixSocketAgent); + return unixSocketAgent; + } + return socketAgents.get(apiPath); + } catch (e) { + console.error('Error parsing url', url); + } } return tcpSocketAgent; } From 52171198038c377a5a07c336427c568f62baec43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Br=C3=A4utigam?= Date: Thu, 14 Dec 2023 14:45:24 +0100 Subject: [PATCH 06/15] fix unit socket connection, remove duplicate folder, form-data is a runtime dependency --- package.json | 2 +- specs/addon.yaml | 2 - src/addon.ts | 21 +- src/addon/core/request.ts | 28 +- src/addon/models/ConfigurationEntry.ts | 2 +- src/core/request.ts | 28 +- src/fhapi/core/request.ts | 29 +- src/index.ts | 2 +- src/rpc/core/request.ts | 29 +- src/scripting/AddonClient.ts | 41 -- src/scripting/core/ApiError.ts | 25 -- src/scripting/core/ApiRequestOptions.ts | 17 - src/scripting/core/ApiResult.ts | 11 - src/scripting/core/BaseHttpRequest.ts | 14 - src/scripting/core/CancelablePromise.ts | 131 ------- src/scripting/core/NodeHttpRequest.ts | 26 -- src/scripting/core/OpenAPI.ts | 32 -- src/scripting/core/request.ts | 356 ------------------ src/scripting/index.ts | 48 --- src/scripting/models/ApplicationState.ts | 8 - src/scripting/models/ApplicationStateEntry.ts | 9 - src/scripting/models/BasicParameter.ts | 22 -- src/scripting/models/ButtonEvent.ts | 12 - src/scripting/models/ChannelParameter.ts | 11 - src/scripting/models/Configuration.ts | 8 - src/scripting/models/ConfigurationEntry.ts | 9 - src/scripting/models/Event.ts | 12 - src/scripting/models/JsonParameter.ts | 11 - src/scripting/models/Metadata.ts | 35 -- src/scripting/models/NumberParameter.ts | 13 - src/scripting/models/Parameter.ts | 13 - src/scripting/models/ParameterEvent.ts | 13 - src/scripting/models/ParameterGroup.ts | 14 - src/scripting/models/Parameters.ts | 8 - src/scripting/models/Reference.ts | 6 - src/scripting/models/References.ts | 8 - src/scripting/models/SelectOption.ts | 12 - src/scripting/models/SelectParameter.ts | 12 - src/scripting/models/Sha256.ts | 6 - src/scripting/models/State.ts | 10 - src/scripting/models/StringParameter.ts | 13 - src/scripting/models/TranslatedString.ts | 25 -- src/scripting/models/TranslatedUri.ts | 27 -- src/scripting/models/Uri.ts | 6 - src/scripting/models/Wizard.ts | 16 - src/scripting/models/WizardEvent.ts | 14 - src/scripting/models/WizardModeCondition.ts | 9 - .../models/WizardParameterCondition.ts | 10 - src/scripting/models/WizardStep.ts | 18 - src/scripting/models/WizardStepEvent.ts | 14 - src/scripting/models/Wizards.ts | 8 - src/scripting/services/ContainerService.ts | 300 --------------- src/scripting/services/ExperimentalService.ts | 37 -- src/scripting/services/RepositoryService.ts | 105 ------ src/serial/core/request.ts | 28 +- src/utilities.ts | 2 +- 56 files changed, 125 insertions(+), 1603 deletions(-) delete mode 100644 src/scripting/AddonClient.ts delete mode 100644 src/scripting/core/ApiError.ts delete mode 100644 src/scripting/core/ApiRequestOptions.ts delete mode 100644 src/scripting/core/ApiResult.ts delete mode 100644 src/scripting/core/BaseHttpRequest.ts delete mode 100644 src/scripting/core/CancelablePromise.ts delete mode 100644 src/scripting/core/NodeHttpRequest.ts delete mode 100644 src/scripting/core/OpenAPI.ts delete mode 100644 src/scripting/core/request.ts delete mode 100644 src/scripting/index.ts delete mode 100644 src/scripting/models/ApplicationState.ts delete mode 100644 src/scripting/models/ApplicationStateEntry.ts delete mode 100644 src/scripting/models/BasicParameter.ts delete mode 100644 src/scripting/models/ButtonEvent.ts delete mode 100644 src/scripting/models/ChannelParameter.ts delete mode 100644 src/scripting/models/Configuration.ts delete mode 100644 src/scripting/models/ConfigurationEntry.ts delete mode 100644 src/scripting/models/Event.ts delete mode 100644 src/scripting/models/JsonParameter.ts delete mode 100644 src/scripting/models/Metadata.ts delete mode 100644 src/scripting/models/NumberParameter.ts delete mode 100644 src/scripting/models/Parameter.ts delete mode 100644 src/scripting/models/ParameterEvent.ts delete mode 100644 src/scripting/models/ParameterGroup.ts delete mode 100644 src/scripting/models/Parameters.ts delete mode 100644 src/scripting/models/Reference.ts delete mode 100644 src/scripting/models/References.ts delete mode 100644 src/scripting/models/SelectOption.ts delete mode 100644 src/scripting/models/SelectParameter.ts delete mode 100644 src/scripting/models/Sha256.ts delete mode 100644 src/scripting/models/State.ts delete mode 100644 src/scripting/models/StringParameter.ts delete mode 100644 src/scripting/models/TranslatedString.ts delete mode 100644 src/scripting/models/TranslatedUri.ts delete mode 100644 src/scripting/models/Uri.ts delete mode 100644 src/scripting/models/Wizard.ts delete mode 100644 src/scripting/models/WizardEvent.ts delete mode 100644 src/scripting/models/WizardModeCondition.ts delete mode 100644 src/scripting/models/WizardParameterCondition.ts delete mode 100644 src/scripting/models/WizardStep.ts delete mode 100644 src/scripting/models/WizardStepEvent.ts delete mode 100644 src/scripting/models/Wizards.ts delete mode 100644 src/scripting/services/ContainerService.ts delete mode 100644 src/scripting/services/ExperimentalService.ts delete mode 100644 src/scripting/services/RepositoryService.ts diff --git a/package.json b/package.json index 6ce34c6..24b0d74 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,6 @@ "@types/node-fetch": "^2.6.6", "abort-controller": "^3.0.0", "cspell": "7.3.8", - "form-data": "^4.0.0", "openapi-typescript-codegen": "^0.25.0", "typescript": "5.2.2" }, @@ -33,6 +32,7 @@ "@types/ws": "^8.5.7", "bufferutil": "^4.0.1", "eventsource": "^2.0.2", + "form-data": "^4.0.0", "isomorphic-ws": "^5.0.0", "json-rpc-2.0": "^1.0.0", "node-fetch": "^2.7.0", diff --git a/specs/addon.yaml b/specs/addon.yaml index 1cb3090..6cb7b5b 100644 --- a/specs/addon.yaml +++ b/specs/addon.yaml @@ -1384,8 +1384,6 @@ components: ConfigurationEntry: type: object - required: - - "items" properties: items: type: object diff --git a/src/addon.ts b/src/addon.ts index 5216e28..78fb39d 100644 --- a/src/addon.ts +++ b/src/addon.ts @@ -1,5 +1,5 @@ import EventSource from 'eventsource'; -import { Metadata, Configuration, ApplicationState, Event, AddonClient, OpenAPIConfig } from './scripting'; +import { Metadata, Configuration, ApplicationState, Event, AddonClient, OpenAPIConfig } from './addon/'; import * as fs from 'fs'; import * as http from 'http'; import * as net from 'net'; @@ -9,6 +9,8 @@ import { handleRequestError } from './utilities'; export {Configuration, ApplicationState}; +const REST_PATH = "/api/scripting/v1"; + export function readMetaData() { const data = fs.readFileSync('free-at-home-metadata.json', 'utf8'); const metaData = JSON.parse(data); @@ -54,6 +56,7 @@ export class AddOn< EventType = Event> extends EventEmitter { private id: string; private eventSourceOptions: EventSourceOptions; + private eventSourceBaseUrl: string = ""; private connectionConfig: Partial; private api: AddonClient; private configurationEventSource?: EventSource; @@ -64,7 +67,7 @@ export class AddOn< super(); this.id = id; const baseUrl = baseUrl_ ?? process.env.FREEATHOME_SCRIPTING_API_BASE_URL - ?? ((process.env.FREEATHOME_BASE_URL) ? process.env.FREEATHOME_BASE_URL + "/api/scripting/v1" : "http://localhost/api/scripting/v1"); + ?? ((process.env.FREEATHOME_BASE_URL) ? process.env.FREEATHOME_BASE_URL + REST_PATH : "http://localhost" + REST_PATH); const username = username_ ?? process.env.FREEATHOME_API_USERNAME ?? "installer"; const password = password_ ?? process.env.FREEATHOME_API_PASSWORD ?? "12345"; const useUnixSocket: boolean = process.env.FREEATHOME_USE_UNIX_SOCKET !== undefined; @@ -73,14 +76,15 @@ export class AddOn< }; function connectToUnixSocket(options: http.ClientRequestArgs, connectionListener?: () => void) { - return net.createConnection("/run/api/scripting/v1", connectionListener); + return net.createConnection("/run" + REST_PATH, connectionListener); } this.connectionConfig = { - BASE: (useUnixSocket) ? "http://localhost" : baseUrl, + BASE: (useUnixSocket) ? "http://localhost" + REST_PATH : baseUrl, USERNAME: username, PASSWORD: password } + this.eventSourceBaseUrl = (useUnixSocket) ? "http://localhost" : baseUrl; this.api = new AddonClient(this.connectionConfig); @@ -97,7 +101,7 @@ export class AddOn< if (undefined !== this.configurationEventSource) return; - const url = this.connectionConfig.BASE + "/rest/container/" + this.id + "/configuration"; + const url = this.eventSourceBaseUrl + "/rest/container/" + this.id + "/configuration"; this.configurationEventSource = new EventSource( url, this.eventSourceOptions); @@ -109,6 +113,7 @@ export class AddOn< console.log("error in event source"); this.emit("configurationConnectionChanged", "error"); console.log(event); + console.log(url); }; this.configurationEventSource.onmessage = (event: MessageEvent) => { try { @@ -125,7 +130,7 @@ export class AddOn< if (undefined !== this.applicationStateEventSource) return; - const url = this.connectionConfig.BASE + "/rest/container/" + this.id + "/applicationstate"; + const url = this.eventSourceBaseUrl + "/rest/container/" + this.id + "/applicationstate"; this.applicationStateEventSource = new EventSource( url, this.eventSourceOptions); @@ -137,6 +142,7 @@ export class AddOn< console.log("error in event source"); this.emit("applicationStateConnectionChanged", "error"); console.log(event); + console.log(url); }; this.applicationStateEventSource.onmessage = (event: MessageEvent) => { try { @@ -153,7 +159,7 @@ export class AddOn< if (undefined !== this.eventEventSource) return; - const url = this.connectionConfig.BASE + "/rest/container/" + this.id + "/events"; + const url = this.eventSourceBaseUrl + "/rest/container/" + this.id + "/events"; this.eventEventSource = new EventSource( url, this.eventSourceOptions); @@ -165,6 +171,7 @@ export class AddOn< console.log("error in event source"); this.emit("eventConnectionChanged", "error"); console.log(event); + console.log(url); }; this.eventEventSource.onmessage = async (event: MessageEvent) => { try { diff --git a/src/addon/core/request.ts b/src/addon/core/request.ts index b0aeaea..a36bbfb 100644 --- a/src/addon/core/request.ts +++ b/src/addon/core/request.ts @@ -101,7 +101,20 @@ const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => { return substring; }); - const url = `${config.BASE}${path}`; + let base = config.BASE; + if (useUnixSocket) { + // for unit sockets the api path is encoded into the base url, for the request we have to remove that + try { + const url = new URL(base); + url.pathname = ""; + base = url.toString(); + if (base.endsWith("/")) { + base = base.substring(0, base.length-1); + } + } catch (e) {} + } + + const url = `${base}${path}`; if (options.query) { return `${url}${getQueryString(options.query)}`; } @@ -210,13 +223,13 @@ const tcpSocketAgent = new http.Agent({ const socketAgents = new Map(); -function getAgent(url: string) { +function getAgent(url: string, basePath: string) { if (useUnixSocket) { try { - const parsedUrl = new URL(url); + const parsedUrl = new URL(basePath); const apiPath = parsedUrl.pathname; if (!socketAgents.has(apiPath)) { - console.log('creating unix socket agent for ', apiPath); + console.log('creating unix socket agent for', apiPath); const unixSocketAgent = new http.Agent({ socketPath: "/run" + apiPath, }); @@ -237,7 +250,8 @@ export const sendRequest = async ( body: any, formData: FormData | undefined, headers: Headers, - onCancel: OnCancel + onCancel: OnCancel, + basePath: string ): Promise => { const controller = new AbortController(); @@ -246,7 +260,7 @@ export const sendRequest = async ( method: options.method, body: body ?? formData, signal: controller.signal as AbortSignal, - agent: getAgent(url) + agent: getAgent(url, basePath) }; onCancel(() => controller.abort()); @@ -334,7 +348,7 @@ export const request = (config: OpenAPIConfig, options: ApiRequestOptions): C const headers = await getHeaders(config, options); if (!onCancel.isCancelled) { - const response = await sendRequest(options, url, body, formData, headers, onCancel); + const response = await sendRequest(options, url, body, formData, headers, onCancel, config.BASE); const responseBody = await getResponseBody(response); const responseHeader = getResponseHeader(response, options.responseHeader); diff --git a/src/addon/models/ConfigurationEntry.ts b/src/addon/models/ConfigurationEntry.ts index 6caeeac..0eca712 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/core/request.ts b/src/core/request.ts index b0aeaea..a36bbfb 100644 --- a/src/core/request.ts +++ b/src/core/request.ts @@ -101,7 +101,20 @@ const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => { return substring; }); - const url = `${config.BASE}${path}`; + let base = config.BASE; + if (useUnixSocket) { + // for unit sockets the api path is encoded into the base url, for the request we have to remove that + try { + const url = new URL(base); + url.pathname = ""; + base = url.toString(); + if (base.endsWith("/")) { + base = base.substring(0, base.length-1); + } + } catch (e) {} + } + + const url = `${base}${path}`; if (options.query) { return `${url}${getQueryString(options.query)}`; } @@ -210,13 +223,13 @@ const tcpSocketAgent = new http.Agent({ const socketAgents = new Map(); -function getAgent(url: string) { +function getAgent(url: string, basePath: string) { if (useUnixSocket) { try { - const parsedUrl = new URL(url); + const parsedUrl = new URL(basePath); const apiPath = parsedUrl.pathname; if (!socketAgents.has(apiPath)) { - console.log('creating unix socket agent for ', apiPath); + console.log('creating unix socket agent for', apiPath); const unixSocketAgent = new http.Agent({ socketPath: "/run" + apiPath, }); @@ -237,7 +250,8 @@ export const sendRequest = async ( body: any, formData: FormData | undefined, headers: Headers, - onCancel: OnCancel + onCancel: OnCancel, + basePath: string ): Promise => { const controller = new AbortController(); @@ -246,7 +260,7 @@ export const sendRequest = async ( method: options.method, body: body ?? formData, signal: controller.signal as AbortSignal, - agent: getAgent(url) + agent: getAgent(url, basePath) }; onCancel(() => controller.abort()); @@ -334,7 +348,7 @@ export const request = (config: OpenAPIConfig, options: ApiRequestOptions): C const headers = await getHeaders(config, options); if (!onCancel.isCancelled) { - const response = await sendRequest(options, url, body, formData, headers, onCancel); + const response = await sendRequest(options, url, body, formData, headers, onCancel, config.BASE); const responseBody = await getResponseBody(response); const responseHeader = getResponseHeader(response, options.responseHeader); diff --git a/src/fhapi/core/request.ts b/src/fhapi/core/request.ts index 6629a04..a36bbfb 100644 --- a/src/fhapi/core/request.ts +++ b/src/fhapi/core/request.ts @@ -101,7 +101,20 @@ const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => { return substring; }); - const url = `${config.BASE}${path}`; + let base = config.BASE; + if (useUnixSocket) { + // for unit sockets the api path is encoded into the base url, for the request we have to remove that + try { + const url = new URL(base); + url.pathname = ""; + base = url.toString(); + if (base.endsWith("/")) { + base = base.substring(0, base.length-1); + } + } catch (e) {} + } + + const url = `${base}${path}`; if (options.query) { return `${url}${getQueryString(options.query)}`; } @@ -210,12 +223,13 @@ const tcpSocketAgent = new http.Agent({ const socketAgents = new Map(); -function getAgent(url: string) { +function getAgent(url: string, basePath: string) { if (useUnixSocket) { try { - const apiPath = "/api/fhapi/v1"; + const parsedUrl = new URL(basePath); + const apiPath = parsedUrl.pathname; if (!socketAgents.has(apiPath)) { - console.log('creating unix socket agent for ', apiPath); + console.log('creating unix socket agent for', apiPath); const unixSocketAgent = new http.Agent({ socketPath: "/run" + apiPath, }); @@ -236,7 +250,8 @@ export const sendRequest = async ( body: any, formData: FormData | undefined, headers: Headers, - onCancel: OnCancel + onCancel: OnCancel, + basePath: string ): Promise => { const controller = new AbortController(); @@ -245,7 +260,7 @@ export const sendRequest = async ( method: options.method, body: body ?? formData, signal: controller.signal as AbortSignal, - agent: getAgent(url) + agent: getAgent(url, basePath) }; onCancel(() => controller.abort()); @@ -333,7 +348,7 @@ export const request = (config: OpenAPIConfig, options: ApiRequestOptions): C const headers = await getHeaders(config, options); if (!onCancel.isCancelled) { - const response = await sendRequest(options, url, body, formData, headers, onCancel); + const response = await sendRequest(options, url, body, formData, headers, onCancel, config.BASE); const responseBody = await getResponseBody(response); const responseHeader = getResponseHeader(response, options.responseHeader); diff --git a/src/index.ts b/src/index.ts index d9f5f48..78dd57c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -76,7 +76,7 @@ export * as Utilities from './virtualChannels/utilities/colorSpaceConversion'; export * as ScriptingHost from './addon' export * as AddOn from './addon' -export * as ScriptingAPI from './scripting' +export * as ScriptingAPI from './addon/' export * as RPC from "./rpcWebsocket"; diff --git a/src/rpc/core/request.ts b/src/rpc/core/request.ts index 321c393..a36bbfb 100644 --- a/src/rpc/core/request.ts +++ b/src/rpc/core/request.ts @@ -101,7 +101,20 @@ const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => { return substring; }); - const url = `${config.BASE}${path}`; + let base = config.BASE; + if (useUnixSocket) { + // for unit sockets the api path is encoded into the base url, for the request we have to remove that + try { + const url = new URL(base); + url.pathname = ""; + base = url.toString(); + if (base.endsWith("/")) { + base = base.substring(0, base.length-1); + } + } catch (e) {} + } + + const url = `${base}${path}`; if (options.query) { return `${url}${getQueryString(options.query)}`; } @@ -210,12 +223,13 @@ const tcpSocketAgent = new http.Agent({ const socketAgents = new Map(); -function getAgent(url: string) { +function getAgent(url: string, basePath: string) { if (useUnixSocket) { try { - const apiPath = "/api/rpc/v1"; + const parsedUrl = new URL(basePath); + const apiPath = parsedUrl.pathname; if (!socketAgents.has(apiPath)) { - console.log('creating unix socket agent for ', apiPath); + console.log('creating unix socket agent for', apiPath); const unixSocketAgent = new http.Agent({ socketPath: "/run" + apiPath, }); @@ -236,7 +250,8 @@ export const sendRequest = async ( body: any, formData: FormData | undefined, headers: Headers, - onCancel: OnCancel + onCancel: OnCancel, + basePath: string ): Promise => { const controller = new AbortController(); @@ -245,7 +260,7 @@ export const sendRequest = async ( method: options.method, body: body ?? formData, signal: controller.signal as AbortSignal, - agent: getAgent(url) + agent: getAgent(url, basePath) }; onCancel(() => controller.abort()); @@ -333,7 +348,7 @@ export const request = (config: OpenAPIConfig, options: ApiRequestOptions): C const headers = await getHeaders(config, options); if (!onCancel.isCancelled) { - const response = await sendRequest(options, url, body, formData, headers, onCancel); + const response = await sendRequest(options, url, body, formData, headers, onCancel, config.BASE); const responseBody = await getResponseBody(response); const responseHeader = getResponseHeader(response, options.responseHeader); diff --git a/src/scripting/AddonClient.ts b/src/scripting/AddonClient.ts deleted file mode 100644 index d4ffc1d..0000000 --- a/src/scripting/AddonClient.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { BaseHttpRequest } from './core/BaseHttpRequest'; -import type { OpenAPIConfig } from './core/OpenAPI'; -import { NodeHttpRequest } from './core/NodeHttpRequest'; - -import { ContainerService } from './services/ContainerService'; -import { ExperimentalService } from './services/ExperimentalService'; -import { RepositoryService } from './services/RepositoryService'; - -type HttpRequestConstructor = new (config: OpenAPIConfig) => BaseHttpRequest; - -export class AddonClient { - - public readonly container: ContainerService; - public readonly experimental: ExperimentalService; - public readonly repository: RepositoryService; - - public readonly request: BaseHttpRequest; - - constructor(config?: Partial, HttpRequest: HttpRequestConstructor = NodeHttpRequest) { - this.request = new HttpRequest({ - BASE: config?.BASE ?? 'https://192.168.2.1/api/addon/v1', - VERSION: config?.VERSION ?? '1', - WITH_CREDENTIALS: config?.WITH_CREDENTIALS ?? false, - CREDENTIALS: config?.CREDENTIALS ?? 'include', - TOKEN: config?.TOKEN, - USERNAME: config?.USERNAME, - PASSWORD: config?.PASSWORD, - HEADERS: config?.HEADERS, - ENCODE_PATH: config?.ENCODE_PATH, - }); - - this.container = new ContainerService(this.request); - this.experimental = new ExperimentalService(this.request); - this.repository = new RepositoryService(this.request); - } -} - diff --git a/src/scripting/core/ApiError.ts b/src/scripting/core/ApiError.ts deleted file mode 100644 index d6b8fcc..0000000 --- a/src/scripting/core/ApiError.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { ApiRequestOptions } from './ApiRequestOptions'; -import type { ApiResult } from './ApiResult'; - -export class ApiError extends Error { - public readonly url: string; - public readonly status: number; - public readonly statusText: string; - public readonly body: any; - public readonly request: ApiRequestOptions; - - constructor(request: ApiRequestOptions, response: ApiResult, message: string) { - super(message); - - this.name = 'ApiError'; - this.url = response.url; - this.status = response.status; - this.statusText = response.statusText; - this.body = response.body; - this.request = request; - } -} diff --git a/src/scripting/core/ApiRequestOptions.ts b/src/scripting/core/ApiRequestOptions.ts deleted file mode 100644 index c19adcc..0000000 --- a/src/scripting/core/ApiRequestOptions.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export type ApiRequestOptions = { - readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH'; - readonly url: string; - readonly path?: Record; - readonly cookies?: Record; - readonly headers?: Record; - readonly query?: Record; - readonly formData?: Record; - readonly body?: any; - readonly mediaType?: string; - readonly responseHeader?: string; - readonly errors?: Record; -}; diff --git a/src/scripting/core/ApiResult.ts b/src/scripting/core/ApiResult.ts deleted file mode 100644 index ad8fef2..0000000 --- a/src/scripting/core/ApiResult.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export type ApiResult = { - readonly url: string; - readonly ok: boolean; - readonly status: number; - readonly statusText: string; - readonly body: any; -}; diff --git a/src/scripting/core/BaseHttpRequest.ts b/src/scripting/core/BaseHttpRequest.ts deleted file mode 100644 index 8da3f4d..0000000 --- a/src/scripting/core/BaseHttpRequest.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { ApiRequestOptions } from './ApiRequestOptions'; -import type { CancelablePromise } from './CancelablePromise'; -import type { OpenAPIConfig } from './OpenAPI'; - -export abstract class BaseHttpRequest { - - constructor(public readonly config: OpenAPIConfig) {} - - public abstract request(options: ApiRequestOptions): CancelablePromise; -} diff --git a/src/scripting/core/CancelablePromise.ts b/src/scripting/core/CancelablePromise.ts deleted file mode 100644 index 55fef85..0000000 --- a/src/scripting/core/CancelablePromise.ts +++ /dev/null @@ -1,131 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export class CancelError extends Error { - - constructor(message: string) { - super(message); - this.name = 'CancelError'; - } - - public get isCancelled(): boolean { - return true; - } -} - -export interface OnCancel { - readonly isResolved: boolean; - readonly isRejected: boolean; - readonly isCancelled: boolean; - - (cancelHandler: () => void): void; -} - -export class CancelablePromise implements Promise { - #isResolved: boolean; - #isRejected: boolean; - #isCancelled: boolean; - readonly #cancelHandlers: (() => void)[]; - readonly #promise: Promise; - #resolve?: (value: T | PromiseLike) => void; - #reject?: (reason?: any) => void; - - constructor( - executor: ( - resolve: (value: T | PromiseLike) => void, - reject: (reason?: any) => void, - onCancel: OnCancel - ) => void - ) { - this.#isResolved = false; - this.#isRejected = false; - this.#isCancelled = false; - this.#cancelHandlers = []; - this.#promise = new Promise((resolve, reject) => { - this.#resolve = resolve; - this.#reject = reject; - - const onResolve = (value: T | PromiseLike): void => { - if (this.#isResolved || this.#isRejected || this.#isCancelled) { - return; - } - this.#isResolved = true; - this.#resolve?.(value); - }; - - const onReject = (reason?: any): void => { - if (this.#isResolved || this.#isRejected || this.#isCancelled) { - return; - } - this.#isRejected = true; - this.#reject?.(reason); - }; - - const onCancel = (cancelHandler: () => void): void => { - if (this.#isResolved || this.#isRejected || this.#isCancelled) { - return; - } - this.#cancelHandlers.push(cancelHandler); - }; - - Object.defineProperty(onCancel, 'isResolved', { - get: (): boolean => this.#isResolved, - }); - - Object.defineProperty(onCancel, 'isRejected', { - get: (): boolean => this.#isRejected, - }); - - Object.defineProperty(onCancel, 'isCancelled', { - get: (): boolean => this.#isCancelled, - }); - - return executor(onResolve, onReject, onCancel as OnCancel); - }); - } - - get [Symbol.toStringTag]() { - return "Cancellable Promise"; - } - - public then( - onFulfilled?: ((value: T) => TResult1 | PromiseLike) | null, - onRejected?: ((reason: any) => TResult2 | PromiseLike) | null - ): Promise { - return this.#promise.then(onFulfilled, onRejected); - } - - public catch( - onRejected?: ((reason: any) => TResult | PromiseLike) | null - ): Promise { - return this.#promise.catch(onRejected); - } - - public finally(onFinally?: (() => void) | null): Promise { - return this.#promise.finally(onFinally); - } - - public cancel(): void { - if (this.#isResolved || this.#isRejected || this.#isCancelled) { - return; - } - this.#isCancelled = true; - if (this.#cancelHandlers.length) { - try { - for (const cancelHandler of this.#cancelHandlers) { - cancelHandler(); - } - } catch (error) { - console.warn('Cancellation threw an error', error); - return; - } - } - this.#cancelHandlers.length = 0; - this.#reject?.(new CancelError('Request aborted')); - } - - public get isCancelled(): boolean { - return this.#isCancelled; - } -} diff --git a/src/scripting/core/NodeHttpRequest.ts b/src/scripting/core/NodeHttpRequest.ts deleted file mode 100644 index 680e6a9..0000000 --- a/src/scripting/core/NodeHttpRequest.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { ApiRequestOptions } from './ApiRequestOptions'; -import { BaseHttpRequest } from './BaseHttpRequest'; -import type { CancelablePromise } from './CancelablePromise'; -import type { OpenAPIConfig } from './OpenAPI'; -import { request as __request } from './request'; - -export class NodeHttpRequest extends BaseHttpRequest { - - constructor(config: OpenAPIConfig) { - super(config); - } - - /** - * Request method - * @param options The request options from the service - * @returns CancelablePromise - * @throws ApiError - */ - public override request(options: ApiRequestOptions): CancelablePromise { - return __request(this.config, options); - } -} diff --git a/src/scripting/core/OpenAPI.ts b/src/scripting/core/OpenAPI.ts deleted file mode 100644 index af951f4..0000000 --- a/src/scripting/core/OpenAPI.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { ApiRequestOptions } from './ApiRequestOptions'; - -type Resolver = (options: ApiRequestOptions) => Promise; -type Headers = Record; - -export type OpenAPIConfig = { - BASE: string; - VERSION: string; - WITH_CREDENTIALS: boolean; - CREDENTIALS: 'include' | 'omit' | 'same-origin'; - TOKEN?: string | Resolver | undefined; - USERNAME?: string | Resolver | undefined; - PASSWORD?: string | Resolver | undefined; - HEADERS?: Headers | Resolver | undefined; - ENCODE_PATH?: ((path: string) => string) | undefined; -}; - -export const OpenAPI: OpenAPIConfig = { - BASE: 'https://192.168.2.1/api/addon/v1', - VERSION: '1', - WITH_CREDENTIALS: false, - CREDENTIALS: 'include', - TOKEN: undefined, - USERNAME: undefined, - PASSWORD: undefined, - HEADERS: undefined, - ENCODE_PATH: undefined, -}; diff --git a/src/scripting/core/request.ts b/src/scripting/core/request.ts deleted file mode 100644 index 20ff64a..0000000 --- a/src/scripting/core/request.ts +++ /dev/null @@ -1,356 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import FormData from 'form-data'; -import fetch, { Headers } from 'node-fetch'; -import type { RequestInit, Response } from 'node-fetch'; -import type { AbortSignal } from 'node-fetch/externals'; - -import { ApiError } from './ApiError'; -import type { ApiRequestOptions } from './ApiRequestOptions'; -import type { ApiResult } from './ApiResult'; -import { CancelablePromise } from './CancelablePromise'; -import type { OnCancel } from './CancelablePromise'; -import type { OpenAPIConfig } from './OpenAPI'; - -import net from 'net'; -import http from 'http'; - -export const isDefined = (value: T | null | undefined): value is Exclude => { - return value !== undefined && value !== null; -}; - -export const isString = (value: any): value is string => { - return typeof value === 'string'; -}; - -export const isStringWithValue = (value: any): value is string => { - return isString(value) && value !== ''; -}; - -export const isBlob = (value: any): value is Blob => { - return ( - typeof value === 'object' && - typeof value.type === 'string' && - typeof value.stream === 'function' && - typeof value.arrayBuffer === 'function' && - typeof value.constructor === 'function' && - typeof value.constructor.name === 'string' && - /^(Blob|File)$/.test(value.constructor.name) && - /^(Blob|File)$/.test(value[Symbol.toStringTag]) - ); -}; - -export const isFormData = (value: any): value is FormData => { - return value instanceof FormData; -}; - -export const base64 = (str: string): string => { - try { - return btoa(str); - } catch (err) { - // @ts-ignore - return Buffer.from(str).toString('base64'); - } -}; - -export const getQueryString = (params: Record): string => { - const qs: string[] = []; - - const append = (key: string, value: any) => { - qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`); - }; - - const process = (key: string, value: any) => { - if (isDefined(value)) { - if (Array.isArray(value)) { - value.forEach(v => { - process(key, v); - }); - } else if (typeof value === 'object') { - Object.entries(value).forEach(([k, v]) => { - process(`${key}[${k}]`, v); - }); - } else { - append(key, value); - } - } - }; - - Object.entries(params).forEach(([key, value]) => { - process(key, value); - }); - - if (qs.length > 0) { - return `?${qs.join('&')}`; - } - - return ''; -}; - -const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => { - const encoder = config.ENCODE_PATH || encodeURI; - - const path = options.url - .replace('{api-version}', config.VERSION) - .replace(/{(.*?)}/g, (substring: string, group: string) => { - if (options.path?.hasOwnProperty(group)) { - return encoder(String(options.path[group])); - } - return substring; - }); - - const url = `${config.BASE}${path}`; - if (options.query) { - return `${url}${getQueryString(options.query)}`; - } - return url; -}; - -export const getFormData = (options: ApiRequestOptions): FormData | undefined => { - if (options.formData) { - const formData = new FormData(); - - const process = (key: string, value: any) => { - if (isString(value) || isBlob(value)) { - formData.append(key, value); - } else { - formData.append(key, JSON.stringify(value)); - } - }; - - Object.entries(options.formData) - .filter(([_, value]) => isDefined(value)) - .forEach(([key, value]) => { - if (Array.isArray(value)) { - value.forEach(v => process(key, v)); - } else { - process(key, value); - } - }); - - return formData; - } - return undefined; -}; - -type Resolver = (options: ApiRequestOptions) => Promise; - -export const resolve = async (options: ApiRequestOptions, resolver?: T | Resolver): Promise => { - if (typeof resolver === 'function') { - return (resolver as Resolver)(options); - } - return resolver; -}; - -export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions): Promise => { - const token = await resolve(options, config.TOKEN); - const username = await resolve(options, config.USERNAME); - const password = await resolve(options, config.PASSWORD); - const additionalHeaders = await resolve(options, config.HEADERS); - - const headers = Object.entries({ - Accept: 'application/json', - ...additionalHeaders, - ...options.headers, - }) - .filter(([_, value]) => isDefined(value)) - .reduce((headers, [key, value]) => ({ - ...headers, - [key]: String(value), - }), {} as Record); - - if (isStringWithValue(token)) { - headers['Authorization'] = `Bearer ${token}`; - } - - if (isStringWithValue(username) && isStringWithValue(password)) { - const credentials = base64(`${username}:${password}`); - headers['Authorization'] = `Basic ${credentials}`; - } - - if (options.body) { - if (options.mediaType) { - headers['Content-Type'] = options.mediaType; - } else if (isBlob(options.body)) { - headers['Content-Type'] = 'application/octet-stream'; - } else if (isString(options.body)) { - headers['Content-Type'] = 'text/plain'; - } else if (!isFormData(options.body)) { - headers['Content-Type'] = 'application/json'; - } - } - - return new Headers(headers); -}; - -export const getRequestBody = (options: ApiRequestOptions): any => { - if (options.body !== undefined) { - if (options.mediaType?.includes('/json')) { - return JSON.stringify(options.body) - } else if (isString(options.body) || isBlob(options.body) || isFormData(options.body)) { - return options.body as any; - } else { - return JSON.stringify(options.body); - } - } - return undefined; -}; - -const useUnixSocket: boolean = process.env.FREEATHOME_USE_UNIX_SOCKET !== undefined; - -export function connectToUnixSocket(apiPath: string, options: http.ClientRequestArgs, connectionListener?: () => void) { - return net.createConnection("/run" + apiPath, connectionListener); -} - -const tcpSocketAgent = new http.Agent({ - maxSockets: 4, -}); - -const socketAgents = new Map(); - -function getAgent(url: string) { - if (useUnixSocket) { - try { - const apiPath = "/api/scripting/v1"; - if (!socketAgents.has(apiPath)) { - console.log('creating unix socket agent for', apiPath); - const unixSocketAgent = new http.Agent({ - socketPath: "/run" + apiPath, - }); - socketAgents.set(apiPath, unixSocketAgent); - return unixSocketAgent; - } - return socketAgents.get(apiPath); - } catch (e) { - console.error('Error parsing url', url); - } - } - return tcpSocketAgent; -} - -export const sendRequest = async ( - options: ApiRequestOptions, - url: string, - body: any, - formData: FormData | undefined, - headers: Headers, - onCancel: OnCancel -): Promise => { - const controller = new AbortController(); - - const request: RequestInit = { - headers, - method: options.method, - body: body ?? formData, - signal: controller.signal as AbortSignal, - agent: getAgent(url) - }; - - onCancel(() => controller.abort()); - - return await fetch(url, request); -}; - -export const getResponseHeader = (response: Response, responseHeader?: string): string | undefined => { - if (responseHeader) { - const content = response.headers.get(responseHeader); - if (isString(content)) { - return content; - } - } - return undefined; -}; - -export const getResponseBody = async (response: Response): Promise => { - if (response.status !== 204) { - try { - const contentType = response.headers.get('Content-Type'); - if (contentType) { - const jsonTypes = ['application/json', 'application/problem+json'] - const isJSON = jsonTypes.some(type => contentType.toLowerCase().startsWith(type)); - if (isJSON) { - return await response.json(); - } else { - return await response.text(); - } - } - } catch (error) { - console.error(error); - } - } - return undefined; -}; - -export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => { - const errors: Record = { - 400: 'Bad Request', - 401: 'Unauthorized', - 403: 'Forbidden', - 404: 'Not Found', - 500: 'Internal Server Error', - 502: 'Bad Gateway', - 503: 'Service Unavailable', - ...options.errors, - } - - const error = errors[result.status]; - if (error) { - throw new ApiError(options, result, error); - } - - if (!result.ok) { - const errorStatus = result.status ?? 'unknown'; - const errorStatusText = result.statusText ?? 'unknown'; - const errorBody = (() => { - try { - return JSON.stringify(result.body, null, 2); - } catch (e) { - return undefined; - } - })(); - - throw new ApiError(options, result, - `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}` - ); - } -}; - -/** - * Request method - * @param config The OpenAPI configuration object - * @param options The request options from the service - * @returns CancelablePromise - * @throws ApiError - */ -export const request = (config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise => { - return new CancelablePromise(async (resolve, reject, onCancel) => { - try { - const url = getUrl(config, options); - const formData = getFormData(options); - const body = getRequestBody(options); - const headers = await getHeaders(config, options); - - if (!onCancel.isCancelled) { - const response = await sendRequest(options, url, body, formData, headers, onCancel); - const responseBody = await getResponseBody(response); - const responseHeader = getResponseHeader(response, options.responseHeader); - - const result: ApiResult = { - url, - ok: response.ok, - status: response.status, - statusText: response.statusText, - body: responseHeader ?? responseBody, - }; - - catchErrorCodes(options, result); - - resolve(result.body); - } - } catch (error) { - reject(error); - } - }); -}; diff --git a/src/scripting/index.ts b/src/scripting/index.ts deleted file mode 100644 index 248ec2a..0000000 --- a/src/scripting/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export { AddonClient } from './AddonClient'; - -export { ApiError } from './core/ApiError'; -export { BaseHttpRequest } from './core/BaseHttpRequest'; -export { CancelablePromise, CancelError } from './core/CancelablePromise'; -export { OpenAPI } from './core/OpenAPI'; -export type { OpenAPIConfig } from './core/OpenAPI'; - -export type { ApplicationState } from './models/ApplicationState'; -export type { ApplicationStateEntry } from './models/ApplicationStateEntry'; -export type { BasicParameter } from './models/BasicParameter'; -export type { ButtonEvent } from './models/ButtonEvent'; -export type { ChannelParameter } from './models/ChannelParameter'; -export type { Configuration } from './models/Configuration'; -export type { ConfigurationEntry } from './models/ConfigurationEntry'; -export type { Event } from './models/Event'; -export type { JsonParameter } from './models/JsonParameter'; -export type { Metadata } from './models/Metadata'; -export type { NumberParameter } from './models/NumberParameter'; -export type { Parameter } from './models/Parameter'; -export type { ParameterEvent } from './models/ParameterEvent'; -export type { ParameterGroup } from './models/ParameterGroup'; -export type { Parameters } from './models/Parameters'; -export type { Reference } from './models/Reference'; -export type { References } from './models/References'; -export type { SelectOption } from './models/SelectOption'; -export type { SelectParameter } from './models/SelectParameter'; -export type { Sha256 } from './models/Sha256'; -export type { State } from './models/State'; -export type { StringParameter } from './models/StringParameter'; -export type { TranslatedString } from './models/TranslatedString'; -export type { TranslatedUri } from './models/TranslatedUri'; -export type { Uri } from './models/Uri'; -export type { Wizard } from './models/Wizard'; -export type { WizardEvent } from './models/WizardEvent'; -export type { WizardModeCondition } from './models/WizardModeCondition'; -export type { WizardParameterCondition } from './models/WizardParameterCondition'; -export type { Wizards } from './models/Wizards'; -export type { WizardStep } from './models/WizardStep'; -export type { WizardStepEvent } from './models/WizardStepEvent'; - -export { ContainerService } from './services/ContainerService'; -export { ExperimentalService } from './services/ExperimentalService'; -export { RepositoryService } from './services/RepositoryService'; diff --git a/src/scripting/models/ApplicationState.ts b/src/scripting/models/ApplicationState.ts deleted file mode 100644 index 0c02718..0000000 --- a/src/scripting/models/ApplicationState.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { ApplicationStateEntry } from './ApplicationStateEntry'; - -export type ApplicationState = Record; diff --git a/src/scripting/models/ApplicationStateEntry.ts b/src/scripting/models/ApplicationStateEntry.ts deleted file mode 100644 index 0c0c108..0000000 --- a/src/scripting/models/ApplicationStateEntry.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -export type ApplicationStateEntry = { - items?: Record; -}; - diff --git a/src/scripting/models/BasicParameter.ts b/src/scripting/models/BasicParameter.ts deleted file mode 100644 index ced23f6..0000000 --- a/src/scripting/models/BasicParameter.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { SelectOption } from './SelectOption'; -import type { TranslatedString } from './TranslatedString'; - -export type BasicParameter = { - name: TranslatedString; - type: 'BasicParameter'; - required?: boolean; - description?: TranslatedString; - default?: (string | number | boolean); - dependsOn?: string; - dependsOnValues?: Array; - dependsOnOptions?: Array<{ - values: Array; - options: SelectOption; - }>; -}; - diff --git a/src/scripting/models/ButtonEvent.ts b/src/scripting/models/ButtonEvent.ts deleted file mode 100644 index 5792328..0000000 --- a/src/scripting/models/ButtonEvent.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -export type ButtonEvent = { - eventType: 'buttonPressed'; - parameter: string; - group: string; - index?: string; -}; - diff --git a/src/scripting/models/ChannelParameter.ts b/src/scripting/models/ChannelParameter.ts deleted file mode 100644 index 75dcf0f..0000000 --- a/src/scripting/models/ChannelParameter.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { BasicParameter } from './BasicParameter'; - -export type ChannelParameter = (BasicParameter & { - multiSelect?: boolean; -}); - diff --git a/src/scripting/models/Configuration.ts b/src/scripting/models/Configuration.ts deleted file mode 100644 index b0d7e31..0000000 --- a/src/scripting/models/Configuration.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { ConfigurationEntry } from './ConfigurationEntry'; - -export type Configuration = Record; diff --git a/src/scripting/models/ConfigurationEntry.ts b/src/scripting/models/ConfigurationEntry.ts deleted file mode 100644 index b22e633..0000000 --- a/src/scripting/models/ConfigurationEntry.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -export type ConfigurationEntry = { - items?: Record; -}; - diff --git a/src/scripting/models/Event.ts b/src/scripting/models/Event.ts deleted file mode 100644 index 1c59656..0000000 --- a/src/scripting/models/Event.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { ButtonEvent } from './ButtonEvent'; -import type { ParameterEvent } from './ParameterEvent'; -import type { WizardEvent } from './WizardEvent'; -import type { WizardStepEvent } from './WizardStepEvent'; - -export type Event = (ButtonEvent | ParameterEvent | WizardEvent | WizardStepEvent); - diff --git a/src/scripting/models/JsonParameter.ts b/src/scripting/models/JsonParameter.ts deleted file mode 100644 index 8f9d07b..0000000 --- a/src/scripting/models/JsonParameter.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { BasicParameter } from './BasicParameter'; - -export type JsonParameter = (BasicParameter & { - json?: (string | Record); -}); - diff --git a/src/scripting/models/Metadata.ts b/src/scripting/models/Metadata.ts deleted file mode 100644 index a4b7aa9..0000000 --- a/src/scripting/models/Metadata.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { Parameters } from './Parameters'; -import type { TranslatedString } from './TranslatedString'; -import type { TranslatedUri } from './TranslatedUri'; -import type { Wizards } from './Wizards'; - -export type Metadata = { - version: string; - id: string; - name: TranslatedString; - description: TranslatedString; - license: string; - author: string; - url: TranslatedUri; - supportUrl?: string; - howtoUrl?: string; - minSysapVersion?: string; - accessControl?: { - allowedAPIs?: Array<'webinterface' | 'serialport'>; - networkAccess?: boolean; - networkPorts?: Array; - }; - entryPoint: string; - type: 'app' | 'runtime' | 'standalone'; - parameters?: Parameters; - wizards?: Wizards; - minAuxFileUploadIntervalMinutes?: number; - organizationId?: string; - rpc?: Array; -}; - diff --git a/src/scripting/models/NumberParameter.ts b/src/scripting/models/NumberParameter.ts deleted file mode 100644 index 97dd39d..0000000 --- a/src/scripting/models/NumberParameter.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { BasicParameter } from './BasicParameter'; - -export type NumberParameter = (BasicParameter & { - min?: number; - max?: number; - unit?: string; -}); - diff --git a/src/scripting/models/Parameter.ts b/src/scripting/models/Parameter.ts deleted file mode 100644 index d2cb286..0000000 --- a/src/scripting/models/Parameter.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { BasicParameter } from './BasicParameter'; -import type { ChannelParameter } from './ChannelParameter'; -import type { NumberParameter } from './NumberParameter'; -import type { SelectParameter } from './SelectParameter'; -import type { StringParameter } from './StringParameter'; - -export type Parameter = (StringParameter | NumberParameter | ChannelParameter | SelectParameter | BasicParameter); - diff --git a/src/scripting/models/ParameterEvent.ts b/src/scripting/models/ParameterEvent.ts deleted file mode 100644 index 7cfb019..0000000 --- a/src/scripting/models/ParameterEvent.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -export type ParameterEvent = { - eventType: 'parameterChanged'; - parameter: string; - group: string; - index?: string; - value: (string | number | boolean); -}; - diff --git a/src/scripting/models/ParameterGroup.ts b/src/scripting/models/ParameterGroup.ts deleted file mode 100644 index ad23d1c..0000000 --- a/src/scripting/models/ParameterGroup.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { Parameter } from './Parameter'; -import type { TranslatedString } from './TranslatedString'; - -export type ParameterGroup = { - name: TranslatedString; - multiple?: boolean; - items: Record; -}; - diff --git a/src/scripting/models/Parameters.ts b/src/scripting/models/Parameters.ts deleted file mode 100644 index 3bee2ee..0000000 --- a/src/scripting/models/Parameters.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { ParameterGroup } from './ParameterGroup'; - -export type Parameters = Record; diff --git a/src/scripting/models/Reference.ts b/src/scripting/models/Reference.ts deleted file mode 100644 index 6df01bd..0000000 --- a/src/scripting/models/Reference.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -export type Reference = string; diff --git a/src/scripting/models/References.ts b/src/scripting/models/References.ts deleted file mode 100644 index afceea6..0000000 --- a/src/scripting/models/References.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { Sha256 } from './Sha256'; - -export type References = Record; diff --git a/src/scripting/models/SelectOption.ts b/src/scripting/models/SelectOption.ts deleted file mode 100644 index 8ccaf24..0000000 --- a/src/scripting/models/SelectOption.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { TranslatedString } from './TranslatedString'; - -export type SelectOption = { - key: string; - name?: TranslatedString; -}; - diff --git a/src/scripting/models/SelectParameter.ts b/src/scripting/models/SelectParameter.ts deleted file mode 100644 index 26a74f9..0000000 --- a/src/scripting/models/SelectParameter.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { BasicParameter } from './BasicParameter'; -import type { SelectOption } from './SelectOption'; - -export type SelectParameter = (BasicParameter & { - options?: Array; -}); - diff --git a/src/scripting/models/Sha256.ts b/src/scripting/models/Sha256.ts deleted file mode 100644 index e927d93..0000000 --- a/src/scripting/models/Sha256.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -export type Sha256 = string; diff --git a/src/scripting/models/State.ts b/src/scripting/models/State.ts deleted file mode 100644 index e2f2a45..0000000 --- a/src/scripting/models/State.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -export type State = { - id?: 'ok' | 'configurationNeeded' | 'error'; - text?: string; -}; - diff --git a/src/scripting/models/StringParameter.ts b/src/scripting/models/StringParameter.ts deleted file mode 100644 index 94b16fe..0000000 --- a/src/scripting/models/StringParameter.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { BasicParameter } from './BasicParameter'; - -export type StringParameter = (BasicParameter & { - min?: number; - max?: number; - regex?: string; -}); - diff --git a/src/scripting/models/TranslatedString.ts b/src/scripting/models/TranslatedString.ts deleted file mode 100644 index 2dae46a..0000000 --- a/src/scripting/models/TranslatedString.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -export type TranslatedString = (string | { - en: string; - es?: string; - fr?: string; - it?: string; - nl?: string; - de?: string; - zh?: string; - da?: string; - fi?: string; - nb?: string; - pl?: string; - pt?: string; - ru?: string; - sv?: string; - el?: string; - cs?: string; - tr?: string; -}); - diff --git a/src/scripting/models/TranslatedUri.ts b/src/scripting/models/TranslatedUri.ts deleted file mode 100644 index 69285c3..0000000 --- a/src/scripting/models/TranslatedUri.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { Uri } from './Uri'; - -export type TranslatedUri = (Uri | { - en: Uri; - es?: Uri; - fr?: Uri; - it?: Uri; - nl?: Uri; - de?: Uri; - zh?: Uri; - da?: Uri; - fi?: Uri; - nb?: Uri; - pl?: Uri; - pt?: Uri; - ru?: Uri; - sv?: Uri; - el?: Uri; - cs?: Uri; - tr?: Uri; -}); - diff --git a/src/scripting/models/Uri.ts b/src/scripting/models/Uri.ts deleted file mode 100644 index b221398..0000000 --- a/src/scripting/models/Uri.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -export type Uri = string; diff --git a/src/scripting/models/Wizard.ts b/src/scripting/models/Wizard.ts deleted file mode 100644 index b100df7..0000000 --- a/src/scripting/models/Wizard.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { TranslatedString } from './TranslatedString'; -import type { WizardStep } from './WizardStep'; - -export type Wizard = { - name: TranslatedString; - create: boolean; - edit: boolean; - sections: Array; - steps: Array; -}; - diff --git a/src/scripting/models/WizardEvent.ts b/src/scripting/models/WizardEvent.ts deleted file mode 100644 index ba83284..0000000 --- a/src/scripting/models/WizardEvent.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -export type WizardEvent = { - eventType: 'wizard'; - wizard: string; - state: 'started' | 'canceled' | 'finished'; - mode: 'create' | 'edit'; - active: boolean; - index?: string; -}; - diff --git a/src/scripting/models/WizardModeCondition.ts b/src/scripting/models/WizardModeCondition.ts deleted file mode 100644 index 0d7128d..0000000 --- a/src/scripting/models/WizardModeCondition.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -export type WizardModeCondition = { - modes: Array<'create' | 'edit'>; -}; - diff --git a/src/scripting/models/WizardParameterCondition.ts b/src/scripting/models/WizardParameterCondition.ts deleted file mode 100644 index cf55cab..0000000 --- a/src/scripting/models/WizardParameterCondition.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -export type WizardParameterCondition = { - parameter: string; - values: Array; -}; - diff --git a/src/scripting/models/WizardStep.ts b/src/scripting/models/WizardStep.ts deleted file mode 100644 index 721205d..0000000 --- a/src/scripting/models/WizardStep.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { Parameter } from './Parameter'; -import type { TranslatedString } from './TranslatedString'; -import type { WizardModeCondition } from './WizardModeCondition'; -import type { WizardParameterCondition } from './WizardParameterCondition'; - -export type WizardStep = { - id: string; - name: TranslatedString; - conditions?: Array<(WizardModeCondition | WizardParameterCondition)>; - steps?: Array; - items?: Record; -}; - diff --git a/src/scripting/models/WizardStepEvent.ts b/src/scripting/models/WizardStepEvent.ts deleted file mode 100644 index db8f5ae..0000000 --- a/src/scripting/models/WizardStepEvent.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -export type WizardStepEvent = { - eventType: 'wizardStep'; - wizard: string; - state: 'entered' | 'exited'; - mode: 'create' | 'edit'; - key: string; - data?: Record; -}; - diff --git a/src/scripting/models/Wizards.ts b/src/scripting/models/Wizards.ts deleted file mode 100644 index a31fc58..0000000 --- a/src/scripting/models/Wizards.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { Wizard } from './Wizard'; - -export type Wizards = Record; diff --git a/src/scripting/services/ContainerService.ts b/src/scripting/services/ContainerService.ts deleted file mode 100644 index 83cfcaa..0000000 --- a/src/scripting/services/ContainerService.ts +++ /dev/null @@ -1,300 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { ApplicationState } from '../models/ApplicationState'; -import type { Configuration } from '../models/Configuration'; -import type { Event } from '../models/Event'; -import type { Metadata } from '../models/Metadata'; -import type { Reference } from '../models/Reference'; -import type { Sha256 } from '../models/Sha256'; - -import type { CancelablePromise } from '../core/CancelablePromise'; -import type { BaseHttpRequest } from '../core/BaseHttpRequest'; - -export class ContainerService { - - constructor(public readonly httpRequest: BaseHttpRequest) {} - - /** - * Create a container instance from a previous uploaded add-on - * Create a container instance from a previous uploaded add-on - * @param requestBody - * @returns Metadata Success - * @throws ApiError - */ - public postContainer( - requestBody?: Sha256, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'POST', - url: '/rest/container', - body: requestBody, - mediaType: 'text/plain', - errors: { - 401: `Authentication information is missing or invalid`, - 502: `Bad Gateway error`, - }, - }); - } - - /** - * List all created container instances - * List all created container instances - * @returns Metadata Success - * @throws ApiError - */ - public getContainers(): CancelablePromise { - return this.httpRequest.request({ - method: 'GET', - url: '/rest/container', - errors: { - 401: `Authentication information is missing or invalid`, - 502: `Bad Gateway error`, - }, - }); - } - - /** - * Get the run time state of a container instance - * List all created container instances - * @param reference hash of commit sha256 - * @returns Metadata Success - * @throws ApiError - */ - public getContainerById( - reference: Reference, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'GET', - url: '/rest/container/{reference}', - path: { - 'reference': reference, - }, - errors: { - 401: `Authentication information is missing or invalid`, - 502: `Bad Gateway error`, - }, - }); - } - - /** - * Delete a container instance by references - * Delete a container by references - * @param reference Container reference - * @returns any Success - * @throws ApiError - */ - public deleteContainer( - reference: Reference, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'DELETE', - url: '/rest/container/{reference}', - path: { - 'reference': reference, - }, - errors: { - 401: `Authentication information is missing or invalid`, - 502: `Bad Gateway error`, - }, - }); - } - - /** - * Get the configuration of a container - * Get the configuration of a container - * @param reference hash of commit sha256 - * @returns Configuration Success - * @throws ApiError - */ - public getContainerConfiguration( - reference: Reference, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'GET', - url: '/rest/container/{reference}/configuration', - path: { - 'reference': reference, - }, - errors: { - 401: `Authentication information is missing or invalid`, - 502: `Bad Gateway error`, - }, - }); - } - - /** - * Set the configuration of a container - * Set the configuration of a container - * @param reference hash of commit sha256 - * @param requestBody - * @returns Reference Success - * @throws ApiError - */ - public setContainerConfiguration( - reference: Reference, - requestBody?: Configuration, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'PUT', - url: '/rest/container/{reference}/configuration', - path: { - 'reference': reference, - }, - body: requestBody, - mediaType: 'application/json', - errors: { - 401: `Authentication information is missing or invalid`, - 502: `Bad Gateway error`, - }, - }); - } - - /** - * Get the application state of a container - * Get the application state of a container - * @param reference hash of commit sha256 - * @returns ApplicationState Success - * @throws ApiError - */ - public getContainerApplicationState( - reference: Reference, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'GET', - url: '/rest/container/{reference}/applicationstate', - path: { - 'reference': reference, - }, - errors: { - 401: `Authentication information is missing or invalid`, - 502: `Bad Gateway error`, - }, - }); - } - - /** - * Set the application state of a container - * Set the application state of a container - * @param reference hash of commit sha256 - * @param requestBody - * @returns Reference Success - * @throws ApiError - */ - public setContainerApplicationState( - reference: Reference, - requestBody?: ApplicationState, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'PUT', - url: '/rest/container/{reference}/applicationstate', - path: { - 'reference': reference, - }, - body: requestBody, - mediaType: 'application/json', - errors: { - 401: `Authentication information is missing or invalid`, - 502: `Bad Gateway error`, - }, - }); - } - - /** - * Get the events of a container - * Get the events of a container - * @param reference hash of commit sha256 - * @returns Event Success - * @throws ApiError - */ - public getContainerEvents( - reference: Reference, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'GET', - url: '/rest/container/{reference}/events', - path: { - 'reference': reference, - }, - errors: { - 401: `Authentication information is missing or invalid`, - 502: `Bad Gateway error`, - }, - }); - } - - /** - * Set the events of a container - * Set the events of a container - * @param reference hash of commit sha256 - * @param requestBody - * @returns Reference Success - * @throws ApiError - */ - public putContainerEvents( - reference: Reference, - requestBody?: Event, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'PUT', - url: '/rest/container/{reference}/events', - path: { - 'reference': reference, - }, - body: requestBody, - mediaType: 'application/json', - errors: { - 401: `Authentication information is missing or invalid`, - 502: `Bad Gateway error`, - }, - }); - } - - /** - * Start a container - * Start a container - * @param reference hash of commit sha256 - * @returns Metadata Success - * @throws ApiError - */ - public startContainer( - reference: Reference, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'POST', - url: '/rest/container/{reference}/start', - path: { - 'reference': reference, - }, - errors: { - 401: `Authentication information is missing or invalid`, - 502: `Bad Gateway error`, - }, - }); - } - - /** - * Stop a running container - * Stop a running container - * @param reference hash of commit sha256 - * @returns Metadata Success - * @throws ApiError - */ - public stopContainer( - reference: Reference, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'POST', - url: '/rest/container/{reference}/stop', - path: { - 'reference': reference, - }, - errors: { - 401: `Authentication information is missing or invalid`, - 502: `Bad Gateway error`, - }, - }); - } - -} diff --git a/src/scripting/services/ExperimentalService.ts b/src/scripting/services/ExperimentalService.ts deleted file mode 100644 index f46d7ee..0000000 --- a/src/scripting/services/ExperimentalService.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Reference } from '../models/Reference'; - -import type { CancelablePromise } from '../core/CancelablePromise'; -import type { BaseHttpRequest } from '../core/BaseHttpRequest'; - -export class ExperimentalService { - - constructor(public readonly httpRequest: BaseHttpRequest) {} - - /** - * queue an add-on url for download - * queue an add-on url for download - * @param requestBody - * @returns Reference Success - * @throws ApiError - */ - public queueDownload( - requestBody?: string, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'POST', - url: '/rest/downloadqueue', - body: requestBody, - mediaType: 'text/plain', - errors: { - 400: `Bad Request`, - 401: `Authentication information is missing or invalid`, - 502: `Bad Gateway error`, - }, - }); - } - -} diff --git a/src/scripting/services/RepositoryService.ts b/src/scripting/services/RepositoryService.ts deleted file mode 100644 index 64b0ac8..0000000 --- a/src/scripting/services/RepositoryService.ts +++ /dev/null @@ -1,105 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Metadata } from '../models/Metadata'; -import type { Reference } from '../models/Reference'; -import type { References } from '../models/References'; - -import type { CancelablePromise } from '../core/CancelablePromise'; -import type { BaseHttpRequest } from '../core/BaseHttpRequest'; - -export class RepositoryService { - - constructor(public readonly httpRequest: BaseHttpRequest) {} - - /** - * Upload an add-on inside of a tar file - * Upload an add-on inside of a tar file - * @param formData - * @returns Reference Success - * @throws ApiError - */ - public upload( - formData?: { - data: Blob; - }, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'POST', - url: '/rest/ref', - formData: formData, - mediaType: 'multipart/form-data', - errors: { - 400: `Bad Request`, - 401: `Authentication information is missing or invalid`, - 502: `Bad Gateway error`, - }, - }); - } - - /** - * Get all add-on references - * Get all an add-on references - * @returns References Success - * @throws ApiError - */ - public getAddOn(): CancelablePromise { - return this.httpRequest.request({ - method: 'GET', - url: '/rest/ref', - errors: { - 401: `Authentication information is missing or invalid`, - 502: `Bad Gateway error`, - }, - }); - } - - /** - * Get Metadata by references - * Get Metadata by references - * @param reference Add-on reference - * @returns Metadata Success - * @throws ApiError - */ - public getMetadata( - reference: Reference, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'GET', - url: '/rest/ref/{reference}', - path: { - 'reference': reference, - }, - errors: { - 401: `Authentication information is missing or invalid`, - 404: `Not Found`, - 502: `Bad Gateway error`, - }, - }); - } - - /** - * Delete an add-on by references - * Delete an add-on by references - * @param reference add-on reference - * @returns any Success - * @throws ApiError - */ - public deleteScript( - reference: Reference, - ): CancelablePromise { - return this.httpRequest.request({ - method: 'DELETE', - url: '/rest/ref/{reference}', - path: { - 'reference': reference, - }, - errors: { - 401: `Authentication information is missing or invalid`, - 502: `Bad Gateway error`, - }, - }); - } - -} diff --git a/src/serial/core/request.ts b/src/serial/core/request.ts index b0aeaea..a36bbfb 100644 --- a/src/serial/core/request.ts +++ b/src/serial/core/request.ts @@ -101,7 +101,20 @@ const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => { return substring; }); - const url = `${config.BASE}${path}`; + let base = config.BASE; + if (useUnixSocket) { + // for unit sockets the api path is encoded into the base url, for the request we have to remove that + try { + const url = new URL(base); + url.pathname = ""; + base = url.toString(); + if (base.endsWith("/")) { + base = base.substring(0, base.length-1); + } + } catch (e) {} + } + + const url = `${base}${path}`; if (options.query) { return `${url}${getQueryString(options.query)}`; } @@ -210,13 +223,13 @@ const tcpSocketAgent = new http.Agent({ const socketAgents = new Map(); -function getAgent(url: string) { +function getAgent(url: string, basePath: string) { if (useUnixSocket) { try { - const parsedUrl = new URL(url); + const parsedUrl = new URL(basePath); const apiPath = parsedUrl.pathname; if (!socketAgents.has(apiPath)) { - console.log('creating unix socket agent for ', apiPath); + console.log('creating unix socket agent for', apiPath); const unixSocketAgent = new http.Agent({ socketPath: "/run" + apiPath, }); @@ -237,7 +250,8 @@ export const sendRequest = async ( body: any, formData: FormData | undefined, headers: Headers, - onCancel: OnCancel + onCancel: OnCancel, + basePath: string ): Promise => { const controller = new AbortController(); @@ -246,7 +260,7 @@ export const sendRequest = async ( method: options.method, body: body ?? formData, signal: controller.signal as AbortSignal, - agent: getAgent(url) + agent: getAgent(url, basePath) }; onCancel(() => controller.abort()); @@ -334,7 +348,7 @@ export const request = (config: OpenAPIConfig, options: ApiRequestOptions): C const headers = await getHeaders(config, options); if (!onCancel.isCancelled) { - const response = await sendRequest(options, url, body, formData, headers, onCancel); + const response = await sendRequest(options, url, body, formData, headers, onCancel, config.BASE); const responseBody = await getResponseBody(response); const responseHeader = getResponseHeader(response, options.responseHeader); diff --git a/src/utilities.ts b/src/utilities.ts index b5f5722..2fe6915 100644 --- a/src/utilities.ts +++ b/src/utilities.ts @@ -6,7 +6,7 @@ import { ApiError as SerialApiError } from "./serial"; import { ApiError as RpcApiError } from "./rpc"; import { ApiError as FahApiError } from "./fhapi"; -import { ApiError as ScriptingApiError } from "./scripting"; +import { ApiError as ScriptingApiError } from "./addon/"; export function binaryIndexOf(list: Array, item: number): number { let min = 0; From 57844588b5b47dd172af5a02a678006e4d94b21c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Br=C3=A4utigam?= Date: Thu, 14 Dec 2023 14:45:52 +0100 Subject: [PATCH 07/15] 0.33.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4943ae3..e62be56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@busch-jaeger/free-at-home", - "version": "0.33.0", + "version": "0.33.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@busch-jaeger/free-at-home", - "version": "0.33.0", + "version": "0.33.1", "license": "ISC", "dependencies": { "@types/ws": "^8.5.7", diff --git a/package.json b/package.json index 24b0d74..2b65130 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@busch-jaeger/free-at-home", - "version": "0.33.0", + "version": "0.33.1", "description": "Library for free@home local device api", "repository": { "type": "git", From 71b0d46b87f05ceb4888eb1d309859b392aa5476 Mon Sep 17 00:00:00 2001 From: Stefan Guelland Date: Thu, 23 Nov 2023 15:36:22 +0000 Subject: [PATCH 08/15] update openapi spec for fhapi --- specs/fhapi.yaml | 123 ++++++++++++++++++++++++++++--- src/fhapi/index.ts | 3 + src/fhapi/models/Device.ts | 2 + src/fhapi/models/Pairing.ts | 11 +++ src/fhapi/models/Pairings.ts | 8 ++ src/fhapi/models/SysAP.ts | 2 + src/fhapi/models/SysapSection.ts | 18 +++++ src/fhapi/services/ApiService.ts | 36 +++++++++ 8 files changed, 194 insertions(+), 9 deletions(-) create mode 100644 src/fhapi/models/Pairing.ts create mode 100644 src/fhapi/models/Pairings.ts create mode 100644 src/fhapi/models/SysapSection.ts diff --git a/specs/fhapi.yaml b/specs/fhapi.yaml index e6d4ba6..1cca883 100644 --- a/specs/fhapi.yaml +++ b/specs/fhapi.yaml @@ -575,6 +575,50 @@ paths: $ref: "#/components/responses/BadGatewayError" "401": $ref: "#/components/responses/UnauthorizedError" + /api/rest/sysap: + get: + tags: + - api + summary: Get sysap related information + description: Get sysap related information + operationId: getsysap + responses: + "200": + description: Sysap + content: + application/json: + schema: + $ref: "#/components/schemas/SysapSection" + "502": + description: free@home error + content: + text/plain: + schema: + $ref: "#/components/schemas/ApiRestConfigurationGet502TextPlainResponse" + "401": + $ref: "#/components/responses/UnauthorizedError" + /api/rest/pairings: + get: + tags: + - api + summary: Get pairings between sensor and actuator + description: Get pairings between sensor and actuator + operationId: getpairings + responses: + "200": + description: Sysap + content: + application/json: + schema: + $ref: "#/components/schemas/Pairings" + "502": + description: free@home error + content: + text/plain: + schema: + $ref: "#/components/schemas/ApiRestConfigurationGet502TextPlainResponse" + "401": + $ref: "#/components/responses/UnauthorizedError" components: schemas: Error: @@ -680,6 +724,10 @@ components: type: string floor: type: string + articleNumber: + type: string + deviceId: + type: string interface: type: string nativeId: @@ -737,9 +785,63 @@ components: role: switch name: foobar-fhapi jid: foobar-fhapi@busch-jaeger.de + SysapSection: + type: object + properties: + sysapName: + type: string + uartSerialNumber: + type: string + testMode: + type: boolean + version: + type: string + sunRiseTimes: + type: array + items: + type: integer + sunSetTimes: + type: array + items: + type: integer + location: + type: object + properties: + latitude: + type: number + longitude: + type: number + required: + - "latitude" + - "longitude" + required: + - "sysapName" + - "uartSerialNumber" + - "testMode" + - "version" + - "sunRiseTimes" + - "sunSetTimes" + Pairing: + type: object + properties: + sensor: + type: string + actuator: + type: string + sceneSlot: + type: integer + required: + - "sensor" + - "actuator" + Pairings: + type: array + items: + $ref: "#/components/schemas/Pairing" SysAP: type: object properties: + sysap: + $ref: "#/components/schemas/SysapSection" devices: $ref: "#/components/schemas/Devices" floorplan: @@ -1033,6 +1135,7 @@ components: devices: $ref: "#/components/schemas/Devices" example: + { "00000000-0000-0000-0000-000000000000": { "devices": @@ -1070,10 +1173,12 @@ components: }, }, "displayName": "Test", - "nativeId": "47110815AA" + "nativeId": "47110815AA", + "unresponsive": false, }, }, - } + }, + } ApiRestDevice_sysap__device_Get502TextPlainResponse: type: string example: FreeAtHome Connection Timeout @@ -1086,8 +1191,7 @@ components: type: array items: type: string - example: - "00000000-0000-0000-0000-000000000000": { "values": ["1"] } + example: { "00000000-0000-0000-0000-000000000000": { "values": ["1"] } } ApiRestDatapoint_sysap__serial_PutRequest: type: string example: "1" @@ -1113,8 +1217,7 @@ components: properties: result: type: string - example: - "00000000-0000-0000-0000-000000000000": { "result": "OK" } + example: { "00000000-0000-0000-0000-000000000000": { "result": "OK" } } ApiRestDatapoint_sysap__serial_Put502TextPlainResponse: type: string example: FreeAtHome Connection Timeout @@ -1132,8 +1235,10 @@ components: serial: type: string example: - "00000000-0000-0000-0000-000000000000": - { "devices": { "abcd12345": { "serial": "6000D2CB27B2" } } } + { + "00000000-0000-0000-0000-000000000000": + { "devices": { "abcd12345": { "serial": "6000D2CB27B2" } } }, + } responses: UnauthorizedError: description: Authentication information is missing or invalid @@ -1173,4 +1278,4 @@ tags: - name: api description: Main API - name: "experimental api" - description: Experimental API \ No newline at end of file + description: Experimental API diff --git a/src/fhapi/index.ts b/src/fhapi/index.ts index 23b5df5..815275d 100644 --- a/src/fhapi/index.ts +++ b/src/fhapi/index.ts @@ -37,9 +37,12 @@ export type { InOutPut } from './models/InOutPut'; export type { NativeSerial } from './models/NativeSerial'; export type { Notification } from './models/Notification'; export type { NotificationContentEntry } from './models/NotificationContentEntry'; +export type { Pairing } from './models/Pairing'; +export type { Pairings } from './models/Pairings'; export type { Rooms } from './models/Rooms'; export type { ScenesTriggered } from './models/ScenesTriggered'; export type { SysAP } from './models/SysAP'; +export type { SysapSection } from './models/SysapSection'; export type { SysapUuid } from './models/SysapUuid'; export type { Users } from './models/Users'; export type { VirtualDevice } from './models/VirtualDevice'; diff --git a/src/fhapi/models/Device.ts b/src/fhapi/models/Device.ts index 2eaf0c2..844dff8 100644 --- a/src/fhapi/models/Device.ts +++ b/src/fhapi/models/Device.ts @@ -9,6 +9,8 @@ export type Device = { displayName?: string; room?: string; floor?: string; + articleNumber?: string; + deviceId?: string; interface?: string; nativeId?: string; channels?: Record; diff --git a/src/fhapi/models/Pairing.ts b/src/fhapi/models/Pairing.ts new file mode 100644 index 0000000..37e18d1 --- /dev/null +++ b/src/fhapi/models/Pairing.ts @@ -0,0 +1,11 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type Pairing = { + sensor: string; + actuator: string; + sceneSlot?: number; +}; + diff --git a/src/fhapi/models/Pairings.ts b/src/fhapi/models/Pairings.ts new file mode 100644 index 0000000..c813e51 --- /dev/null +++ b/src/fhapi/models/Pairings.ts @@ -0,0 +1,8 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { Pairing } from './Pairing'; + +export type Pairings = Array; diff --git a/src/fhapi/models/SysAP.ts b/src/fhapi/models/SysAP.ts index 4619c79..e3107a1 100644 --- a/src/fhapi/models/SysAP.ts +++ b/src/fhapi/models/SysAP.ts @@ -6,12 +6,14 @@ import type { Devices } from './Devices'; import type { Error } from './Error'; import type { Floors } from './Floors'; +import type { SysapSection } from './SysapSection'; import type { Users } from './Users'; /** * SysAP */ export type SysAP = { + sysap?: SysapSection; devices?: Devices; floorplan?: { floors?: Floors; diff --git a/src/fhapi/models/SysapSection.ts b/src/fhapi/models/SysapSection.ts new file mode 100644 index 0000000..26b1fda --- /dev/null +++ b/src/fhapi/models/SysapSection.ts @@ -0,0 +1,18 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type SysapSection = { + sysapName: string; + uartSerialNumber: string; + testMode: boolean; + version: string; + sunRiseTimes: Array; + sunSetTimes: Array; + location?: { + latitude: number; + longitude: number; + }; +}; + diff --git a/src/fhapi/services/ApiService.ts b/src/fhapi/services/ApiService.ts index a5eff9a..4963373 100644 --- a/src/fhapi/services/ApiService.ts +++ b/src/fhapi/services/ApiService.ts @@ -16,6 +16,8 @@ import type { Devicelist } from '../models/Devicelist'; import type { DeviceSerial } from '../models/DeviceSerial'; import type { NativeSerial } from '../models/NativeSerial'; import type { Notification } from '../models/Notification'; +import type { Pairings } from '../models/Pairings'; +import type { SysapSection } from '../models/SysapSection'; import type { SysapUuid } from '../models/SysapUuid'; import type { VirtualDevice } from '../models/VirtualDevice'; import type { VirtualDevicesSuccess } from '../models/VirtualDevicesSuccess'; @@ -292,4 +294,38 @@ export class ApiService { }); } + /** + * Get sysap related information + * Get sysap related information + * @returns SysapSection Sysap + * @throws ApiError + */ + public getsysap(): CancelablePromise { + return this.httpRequest.request({ + method: 'GET', + url: '/api/rest/sysap', + errors: { + 401: `Authentication information is missing or invalid`, + 502: `free@home error`, + }, + }); + } + + /** + * Get pairings between sensor and actuator + * Get pairings between sensor and actuator + * @returns Pairings Sysap + * @throws ApiError + */ + public getpairings(): CancelablePromise { + return this.httpRequest.request({ + method: 'GET', + url: '/api/rest/pairings', + errors: { + 401: `Authentication information is missing or invalid`, + 502: `free@home error`, + }, + }); + } + } From 593c51ccb4171bc547648b18b14b3a3d854308b3 Mon Sep 17 00:00:00 2001 From: Stefan Guelland Date: Wed, 17 Jan 2024 15:44:03 +0000 Subject: [PATCH 09/15] add wrapper to access pairings --- src/freeAtHomeApi.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/freeAtHomeApi.ts b/src/freeAtHomeApi.ts index 5304e58..1701fb0 100644 --- a/src/freeAtHomeApi.ts +++ b/src/freeAtHomeApi.ts @@ -411,6 +411,10 @@ export class FreeAtHomeApi extends (EventEmitter as { new(): Emitter }) { public async postNotification(notification: API.Notification) { return this.apiClient.api.postnotification(notification); } + + public async getPairings(): Promise { + return this.apiClient.api.getpairings(); + } } From 782e9895f4a539bee39545d2cf2380dc149145b1 Mon Sep 17 00:00:00 2001 From: Stefan Guelland Date: Wed, 17 Jan 2024 15:44:40 +0000 Subject: [PATCH 10/15] 0.33.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e62be56..29ad631 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@busch-jaeger/free-at-home", - "version": "0.33.1", + "version": "0.33.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@busch-jaeger/free-at-home", - "version": "0.33.1", + "version": "0.33.2", "license": "ISC", "dependencies": { "@types/ws": "^8.5.7", diff --git a/package.json b/package.json index 2b65130..d2b4fd0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@busch-jaeger/free-at-home", - "version": "0.33.1", + "version": "0.33.2", "description": "Library for free@home local device api", "repository": { "type": "git", From ceb6a947e3b50f50d22175a9bad358e32ae110a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Br=C3=A4utigam?= Date: Thu, 18 Jan 2024 11:47:12 +0100 Subject: [PATCH 11/15] add more features for addon settings, also add missing parts to the spec --- docs/Metadata.md | 92 ++++++++++++++++++- specs/addon.yaml | 126 ++++++++++++++++++++++++++- src/addon/index.ts | 3 + src/addon/models/BasicParameter.ts | 31 ++++++- src/addon/models/DisplayDependsOn.ts | 13 +++ src/addon/models/Metadata.ts | 2 + src/addon/models/ParameterEvent.ts | 3 +- src/addon/models/ParameterGroup.ts | 9 +- src/addon/models/ParameterType.ts | 17 ++++ src/addon/models/SelectOption.ts | 5 ++ src/addon/models/TranslatedButton.ts | 26 ++++++ 11 files changed, 316 insertions(+), 11 deletions(-) create mode 100644 src/addon/models/DisplayDependsOn.ts create mode 100644 src/addon/models/ParameterType.ts create mode 100644 src/addon/models/TranslatedButton.ts diff --git a/docs/Metadata.md b/docs/Metadata.md index 13a38d9..ffc0ea5 100644 --- a/docs/Metadata.md +++ b/docs/Metadata.md @@ -321,7 +321,7 @@ Possible types are: } ``` - If the user selects the `Power` entry, the stored value would be the path to that value: `Energy.Power`. + If the user selects the `Power` entry, the stored value would be the path to that value: `Energy.Power`. ##### Read-only types @@ -329,6 +329,10 @@ Possible types are: The specified text is displayed to the user. +- `button` + + Shows a button that sends an event of type `buttonPressed` event to the Addon. + - `error` > **NOTE:** Requires free@home app version >= 2.4.0 @@ -355,6 +359,50 @@ Possible types are: Only useful for parameter groups with `multiple: true`. The UI generates a UUID when a new entry is created. This UUID can be used to identify the entry. +##### Custom types + +For more complex parameter values you can define custom types. Those types are defined in an extra `types`-section of your metadata and have the same +syntax as a parameter group with just two differences: + +1. The `display` attribute is required +2. The `multiple` attribute is not allowed + +Basic exampe: + + ```json + "parameters": { + "example": { + "name": "Custom example", + "items": { + "register": { + "name": "Register", + "type": "custom", + "customTypeName": "modbusRegister" + } + } + } + } + "types": { + "modbusRegister": { + "name": "Modbus register", + "name@de": "Modbus-Register", + "display": { + "title": "$address", + "error": "$errorMessage" + }, + "items": { + "function": { + ... + }, + "address": { + ... + } + ... + } + } + } + ``` + #### Additional parameter attributes Beside the already explained `name`, `type` and `description` attributes there are some special attributes that can be optionally added to a parameter definition: @@ -373,12 +421,32 @@ Beside the already explained `name`, `type` and `description` attributes there a The value of this parameter is not saved in the settings but send as an event when the user clicks on the send button that is show underneath this parameter. +- `eventScope` + + Defines the amount of information that is sent in the `event`. + + `eventScope: parameter`: [default] sends only the current value of this parameters. + `eventScope: group`: sends the current values of all parameters of the same group. + + - `visible` > **NOTE:** Requires free@home app version >= 2.4.0 Show/Hide this parameter in the UI. (default: `true`) +- `preFill` + + > **NOTE:** Requires free@home app version >= 2.4.0 + + Prefill this value with the last edited one (or one from another config entry of this group). (default: `false`). + +- `saveOnChange` + + > **NOTE:** Requires free@home app version >= 2.4.0 + + When this is true the every change of the parameters value will be saved immediately (default: false) + - `dependsOn` > **NOTE:** Requires free@home app version >= 2.4.0 @@ -472,7 +540,7 @@ Beside the already explained `name`, `type` and `description` attributes there a > **NOTE:** Requires free@home app version >= 2.4.0 - If this is added with a `true` value this parameter is only shown, when the app is in debug mode. + If this is added with a `true` value this parameter is only shown, when the app is in debug mode and the 'Enable Addon debug settings' checkbox is checked in the debug menu. - `rpc`, `rpcCallOn`, `rpcAdditionalParameters` @@ -486,6 +554,8 @@ Beside the already explained `name`, `type` and `description` attributes there a `rpcCallOn: everyChange`: sends it every time any of the other parameter values in the same group has been changed by the user. + `rpcCallOn: buttonPressed`: Only for parameters that have `sendAsEvent: true`. Executes the rpc when the send button has been pressed. + `rpcAdditionalParameters`: is just an optional set of values that are added to the rpc. Full example: If you have a parameter group that configures something that can be explained best with a graph (e.g. a dimming curve or a color temperature calculation that changes over the day) you can use the `svg` parameter type together with these rpc settings to show the user a graph based on your current parameter settings. @@ -599,7 +669,23 @@ and an error line with `error`, a full example would be: }, ``` -The display feature is mostly usefull for parameter groups with the multiple flag. +The display feature is mostly useful for parameter groups with the multiple flag. You are also able to modify the values of `title`, `subtitle` and +`error` depending on values of other parameters in this group. This is familiar with the `dependsOnConfig` feature of a parameter but uses a slightly +different syntax. Example: + +```json +"display": { + "title": "$meterType: $topic", + "subtitle": "Connection: $state", + "subtitle@de": "Verbindung: $state", + "error": "$errorMessage", + "dependsOn": { + "topic": [{"values": [""], "title": "Unconfigured", "title@de": "Unkonfiguriert"}] + } +}, +``` + +In this case the display title will show "Unconfigured" when the topic is empty. #### Using parameters in an ABB free@home Addon diff --git a/specs/addon.yaml b/specs/addon.yaml index 6cb7b5b..59ccfc9 100644 --- a/specs/addon.yaml +++ b/specs/addon.yaml @@ -594,6 +594,10 @@ components: $ref: "#/components/schemas/Parameters" wizards: $ref: "#/components/schemas/Wizards" + types: + type: object + additionalProperties: + $ref: "#/components/schemas/ParameterType" minAuxFileUploadIntervalMinutes: type: integer minimum: 15 @@ -640,16 +644,62 @@ components: type: string multiple: type: boolean + debug: + type: boolean + description: Show this parameter group only when debugging is enabled display: allOf: - $ref: "#/components/schemas/TranslatedTitle" - $ref: "#/components/schemas/TranslatedSubtitle" - $ref: "#/components/schemas/TranslatedError" + - type: object + properties: + dependsOn: + type: object + additionalProperties: + $ref: "#/components/schemas/DisplayDependsOn" + items: type: object additionalProperties: $ref: "#/components/schemas/Parameter" + DisplayDependsOn: + allOf: + - $ref: "#/components/schemas/TranslatedTitle" + - $ref: "#/components/schemas/TranslatedSubtitle" + - $ref: "#/components/schemas/TranslatedError" + - type: object + required: + - "values" + properties: + values: + type: array + items: + type: string + + + ParameterType: + allOf: + - $ref: "#/components/schemas/TranslatedName" + - type: object + required: + - "name" + - "display" + - "items" + properties: + name: + type: string + display: + allOf: + - $ref: "#/components/schemas/TranslatedTitle" + - $ref: "#/components/schemas/TranslatedSubtitle" + - $ref: "#/components/schemas/TranslatedError" + items: + type: object + additionalProperties: + $ref: "#/components/schemas/Parameter" + Wizard: allOf: - $ref: "#/components/schemas/TranslatedName" @@ -769,6 +819,7 @@ components: array: "#/components/schemas/ArrayParameter" svg: "#/components/schemas/BasicParameter" uuid: "#/components/schemas/EmptyParameter" + custom: "#/components/schemas/BasicParameter" EmptyParameter: type: object @@ -844,6 +895,7 @@ components: allOf: - $ref: "#/components/schemas/TranslatedName" - $ref: "#/components/schemas/TranslatedDescription" + - $ref: "#/components/schemas/TranslatedButton" - type: object required: - "name" @@ -876,6 +928,7 @@ components: - "array" - "svg" - "uuid" + - "custom" name: type: string required: @@ -884,7 +937,10 @@ components: anyOf: - type: string - type: number - - type: boolean + - type: boolean + preFill: + type: boolean + description: Prefill this value with the last edited one visible: type: boolean dependsOn: @@ -900,6 +956,7 @@ components: enum: - "initial" - "everyChange" + - "buttonPressed" rpcAdditionalParameters: type: object additionalProperties: true @@ -914,7 +971,24 @@ components: type: array items: $ref: "#/components/schemas/BasicDependsOnConfig" - + customTypeName: + type: string + description: Only relevant when type == custom + event: + type: boolean + description: Do not store this value but send it as an event when the button is pressed + eventScope: + type: string + description: parameter-scope (default) -> send only this parameter value, group-scope -> send all parameter values from the same group + enum: + - "parameter" + - "group" + saveOnChange: + type: boolean + description: "When this is true the every change of the parameters value will be saved immediately (default: false)" + debug: + type: boolean + description: Show this parameter only when debugging is enabled StringParameter: allOf: @@ -1094,6 +1168,11 @@ components: - type: string - type: number - type: boolean + name: + type: string + debug: + type: boolean + description: Show this option only when debugging is enabled TranslatedString: oneOf: @@ -1335,6 +1414,46 @@ components: error@tr: type: string + TranslatedButton: + type: object + properties: + buttonLabel: + type: string + buttonLabel@en: + type: string + buttonLabel@es: + type: string + buttonLabel@fr: + type: string + buttonLabel@it: + type: string + buttonLabel@nl: + type: string + buttonLabel@de: + type: string + buttonLabel@zh: + type: string + buttonLabel@da: + type: string + buttonLabel@fi: + type: string + buttonLabel@nb: + type: string + buttonLabel@pl: + type: string + buttonLabel@pt: + type: string + buttonLabel@ru: + type: string + buttonLabel@sv: + type: string + buttonLabel@el: + type: string + buttonLabel@cs: + type: string + buttonLabel@tr: + type: string + Uri: type: string format: uri @@ -1447,7 +1566,6 @@ components: - "eventType" - "parameter" - "group" - - "value" properties: eventType: type: string @@ -1464,6 +1582,8 @@ components: - type: string - type: number - type: boolean + data: + type: object WizardEvent: type: object diff --git a/src/addon/index.ts b/src/addon/index.ts index b7abffa..6d931dc 100644 --- a/src/addon/index.ts +++ b/src/addon/index.ts @@ -24,6 +24,7 @@ export type { Configuration } from './models/Configuration'; export type { ConfigurationEntry } from './models/ConfigurationEntry'; export type { Datapoints } from './models/Datapoints'; export type { DescriptionParameter } from './models/DescriptionParameter'; +export type { DisplayDependsOn } from './models/DisplayDependsOn'; export type { EmptyParameter } from './models/EmptyParameter'; export type { Event } from './models/Event'; export type { JsonParameter } from './models/JsonParameter'; @@ -33,6 +34,7 @@ export type { Parameter } from './models/Parameter'; export type { ParameterEvent } from './models/ParameterEvent'; export type { ParameterGroup } from './models/ParameterGroup'; export type { Parameters } from './models/Parameters'; +export type { ParameterType } from './models/ParameterType'; export type { Reference } from './models/Reference'; export type { References } from './models/References'; export type { SelectOption } from './models/SelectOption'; @@ -40,6 +42,7 @@ export type { SelectParameter } from './models/SelectParameter'; export type { SeparatorParameter } from './models/SeparatorParameter'; export type { State } from './models/State'; export type { StringParameter } from './models/StringParameter'; +export type { TranslatedButton } from './models/TranslatedButton'; export type { TranslatedDescription } from './models/TranslatedDescription'; export type { TranslatedError } from './models/TranslatedError'; export type { TranslatedName } from './models/TranslatedName'; diff --git a/src/addon/models/BasicParameter.ts b/src/addon/models/BasicParameter.ts index a609e8a..7882112 100644 --- a/src/addon/models/BasicParameter.ts +++ b/src/addon/models/BasicParameter.ts @@ -4,20 +4,45 @@ /* eslint-disable */ import type { BasicDependsOnConfig } from './BasicDependsOnConfig'; +import type { TranslatedButton } from './TranslatedButton'; import type { TranslatedDescription } from './TranslatedDescription'; import type { TranslatedName } from './TranslatedName'; -export type BasicParameter = (TranslatedName & TranslatedDescription & { - type: 'string' | 'multilinestring' | 'password' | 'number' | 'boolean' | 'date' | 'time' | 'weekdays' | 'ipv4' | 'floor' | 'room' | 'channel' | 'select' | 'button' | 'text' | 'error' | 'description' | 'displayQRCode' | 'scanQRCode' | 'hidden' | 'jsonSelector' | 'array' | 'svg' | 'uuid'; +export type BasicParameter = (TranslatedName & TranslatedDescription & TranslatedButton & { + type: 'string' | 'multilinestring' | 'password' | 'number' | 'boolean' | 'date' | 'time' | 'weekdays' | 'ipv4' | 'floor' | 'room' | 'channel' | 'select' | 'button' | 'text' | 'error' | 'description' | 'displayQRCode' | 'scanQRCode' | 'hidden' | 'jsonSelector' | 'array' | 'svg' | 'uuid' | 'custom'; name: string; required?: boolean; default?: (string | number | boolean); + /** + * Prefill this value with the last edited one + */ + preFill?: boolean; visible?: boolean; dependsOn?: string; rpc?: 'getParameterValue' | 'getParameterConfig'; - rpcCallOn?: 'initial' | 'everyChange'; + rpcCallOn?: 'initial' | 'everyChange' | 'buttonPressed'; rpcAdditionalParameters?: Record; dependsOnValues?: Array<(string | number | boolean)>; dependsOnConfig?: Array; + /** + * Only relevant when type == custom + */ + customTypeName?: string; + /** + * Do not store this value but send it as an event when the button is pressed + */ + event?: boolean; + /** + * parameter-scope (default) -> send only this parameter value, group-scope -> send all parameter values from the same group + */ + eventScope?: 'parameter' | 'group'; + /** + * When this is true the every change of the parameters value will be saved immediately (default: false) + */ + saveOnChange?: boolean; + /** + * Show this parameter only when debugging is enabled + */ + debug?: boolean; }); diff --git a/src/addon/models/DisplayDependsOn.ts b/src/addon/models/DisplayDependsOn.ts new file mode 100644 index 0000000..31831f4 --- /dev/null +++ b/src/addon/models/DisplayDependsOn.ts @@ -0,0 +1,13 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { TranslatedError } from './TranslatedError'; +import type { TranslatedSubtitle } from './TranslatedSubtitle'; +import type { TranslatedTitle } from './TranslatedTitle'; + +export type DisplayDependsOn = (TranslatedTitle & TranslatedSubtitle & TranslatedError & { + values: Array; +}); + diff --git a/src/addon/models/Metadata.ts b/src/addon/models/Metadata.ts index 9f9a7c2..a8fbf43 100644 --- a/src/addon/models/Metadata.ts +++ b/src/addon/models/Metadata.ts @@ -4,6 +4,7 @@ /* eslint-disable */ import type { Parameters } from './Parameters'; +import type { ParameterType } from './ParameterType'; import type { TranslatedString } from './TranslatedString'; import type { TranslatedUri } from './TranslatedUri'; import type { Wizards } from './Wizards'; @@ -29,6 +30,7 @@ export type Metadata = { beta?: boolean; parameters?: Parameters; wizards?: Wizards; + types?: Record; minAuxFileUploadIntervalMinutes?: number; organizationId?: string; rpc?: Array; diff --git a/src/addon/models/ParameterEvent.ts b/src/addon/models/ParameterEvent.ts index 7cfb019..e17c5b8 100644 --- a/src/addon/models/ParameterEvent.ts +++ b/src/addon/models/ParameterEvent.ts @@ -8,6 +8,7 @@ export type ParameterEvent = { parameter: string; group: string; index?: string; - value: (string | number | boolean); + value?: (string | number | boolean); + data?: Record; }; diff --git a/src/addon/models/ParameterGroup.ts b/src/addon/models/ParameterGroup.ts index d988422..621149b 100644 --- a/src/addon/models/ParameterGroup.ts +++ b/src/addon/models/ParameterGroup.ts @@ -3,6 +3,7 @@ /* tslint:disable */ /* eslint-disable */ +import type { DisplayDependsOn } from './DisplayDependsOn'; import type { Parameter } from './Parameter'; import type { TranslatedError } from './TranslatedError'; import type { TranslatedName } from './TranslatedName'; @@ -12,7 +13,13 @@ import type { TranslatedTitle } from './TranslatedTitle'; export type ParameterGroup = (TranslatedName & { name: string; multiple?: boolean; - display?: (TranslatedTitle & TranslatedSubtitle & TranslatedError); + /** + * Show this parameter group only when debugging is enabled + */ + debug?: boolean; + display?: (TranslatedTitle & TranslatedSubtitle & TranslatedError & { + dependsOn?: Record; + }); items: Record; }); diff --git a/src/addon/models/ParameterType.ts b/src/addon/models/ParameterType.ts new file mode 100644 index 0000000..c17ebbd --- /dev/null +++ b/src/addon/models/ParameterType.ts @@ -0,0 +1,17 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { Parameter } from './Parameter'; +import type { TranslatedError } from './TranslatedError'; +import type { TranslatedName } from './TranslatedName'; +import type { TranslatedSubtitle } from './TranslatedSubtitle'; +import type { TranslatedTitle } from './TranslatedTitle'; + +export type ParameterType = (TranslatedName & { + name: string; + display: (TranslatedTitle & TranslatedSubtitle & TranslatedError); + items: Record; +}); + diff --git a/src/addon/models/SelectOption.ts b/src/addon/models/SelectOption.ts index b496cc1..9a63c2b 100644 --- a/src/addon/models/SelectOption.ts +++ b/src/addon/models/SelectOption.ts @@ -7,5 +7,10 @@ import type { TranslatedName } from './TranslatedName'; export type SelectOption = (TranslatedName & { key: (string | number | boolean); + name?: string; + /** + * Show this option only when debugging is enabled + */ + debug?: boolean; }); diff --git a/src/addon/models/TranslatedButton.ts b/src/addon/models/TranslatedButton.ts new file mode 100644 index 0000000..3cd1ef7 --- /dev/null +++ b/src/addon/models/TranslatedButton.ts @@ -0,0 +1,26 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type TranslatedButton = { + buttonLabel?: string; + 'buttonLabel@en'?: string; + 'buttonLabel@es'?: string; + 'buttonLabel@fr'?: string; + 'buttonLabel@it'?: string; + 'buttonLabel@nl'?: string; + 'buttonLabel@de'?: string; + 'buttonLabel@zh'?: string; + 'buttonLabel@da'?: string; + 'buttonLabel@fi'?: string; + 'buttonLabel@nb'?: string; + 'buttonLabel@pl'?: string; + 'buttonLabel@pt'?: string; + 'buttonLabel@ru'?: string; + 'buttonLabel@sv'?: string; + 'buttonLabel@el'?: string; + 'buttonLabel@cs'?: string; + 'buttonLabel@tr'?: string; +}; + From 68b5045d038896979520ff1dc9e792c172b71b72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Br=C3=A4utigam?= Date: Mon, 22 Jan 2024 09:35:43 +0100 Subject: [PATCH 12/15] add wrapper to access sysap section --- src/freeAtHomeApi.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/freeAtHomeApi.ts b/src/freeAtHomeApi.ts index 1701fb0..39e83f7 100644 --- a/src/freeAtHomeApi.ts +++ b/src/freeAtHomeApi.ts @@ -415,6 +415,10 @@ export class FreeAtHomeApi extends (EventEmitter as { new(): Emitter }) { public async getPairings(): Promise { return this.apiClient.api.getpairings(); } + + public async getSysapSection(): Promise { + return this.apiClient.api.getsysap(); + } } From 88b4af0f74b92ae6f32501a58feba5b692da0d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Br=C3=A4utigam?= Date: Mon, 22 Jan 2024 09:49:26 +0100 Subject: [PATCH 13/15] 0.34.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 29ad631..f7a7e0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@busch-jaeger/free-at-home", - "version": "0.33.2", + "version": "0.34.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@busch-jaeger/free-at-home", - "version": "0.33.2", + "version": "0.34.0", "license": "ISC", "dependencies": { "@types/ws": "^8.5.7", diff --git a/package.json b/package.json index d2b4fd0..4c88ece 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@busch-jaeger/free-at-home", - "version": "0.33.2", + "version": "0.34.0", "description": "Library for free@home local device api", "repository": { "type": "git", From 9667ff40bd7f484f94727ee5372894423c4de0d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Br=C3=A4utigam?= Date: Thu, 25 Jan 2024 08:01:16 +0100 Subject: [PATCH 14/15] add support for errors and fixed feature --- docs/Metadata.md | 35 +++++++++++++++++++++++++++ docs/img/metadata/internal_error.png | Bin 0 -> 21095 bytes specs/addon.yaml | 20 +++++++++++++++ src/addon/index.ts | 1 + src/addon/models/BasicParameter.ts | 4 +++ src/addon/models/Error.ts | 13 ++++++++++ src/addon/models/Metadata.ts | 2 ++ 7 files changed, 75 insertions(+) create mode 100644 docs/img/metadata/internal_error.png create mode 100644 src/addon/models/Error.ts diff --git a/docs/Metadata.md b/docs/Metadata.md index ffc0ea5..1bfb9ab 100644 --- a/docs/Metadata.md +++ b/docs/Metadata.md @@ -361,6 +361,8 @@ Possible types are: ##### Custom types + > **NOTE:** Requires free@home app version >= 2.4.0 + For more complex parameter values you can define custom types. Those types are defined in an extra `types`-section of your metadata and have the same syntax as a parameter group with just two differences: @@ -453,6 +455,12 @@ Beside the already explained `name`, `type` and `description` attributes there a Allows to define that a parameter is only shown, when another parameter has a specific value. Also requires the additional `dependsOnValues` attribute. Example: +- `fixed` + + > **NOTE:** Requires free@home app version >= 2.4.0 + + 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. + ```json "connectionType": { "name": "Connection", @@ -696,6 +704,33 @@ the Addon. The configured value will show up in the `Configuration`of the Addon, Please see the [writing Addons section](Writing-addons) for more information about using the configuration parameters in the Addon. +### Errors + + > **NOTE:** Requires free@home app version >= 2.4.0 + +Define custom error messages that the UI can show e.g. when the Addon responds to a RPC with an error. + +Basic exampe: + +```json +"errors": { + "CODE_4": { + "name": "Internal error", + "name@de": "Fehler im Gerät", + "description": "Device reports internal error. Please check all settings especially `Modbus ID`, 'Function' and 'Datatype'", + "description@de": "Gerät meldet internen Fehler. Bitte überrüfen sie alle Einstellungen, insb. `Modbus ID`, 'Funktion' und 'Datentyp'" + } +``` + +The addon can respond to a RPC with one of the defined error codes and the UI shows the translated error & description. +Currently this is implemented only for the parameter RPC `getParameterValue`. In order to show the error from the example, the addon +has to respond with ```{"error": "ADDON_ERROR:CODE_1"}```. + +The Addon can also respond with a custom error message, that will be shown as is in the UI. +```{"error": "This is a custom error"}```. In that case a translation is not possible, also there will be no possibility to show additional information below the error as its done with the `description` from a predefined error. The box with the red background will not be visible when you send a custom error message. + +![Screenshot of error in the app](img/metadata/internal_error.png) + ### ABB free@home Addon wizards > **NOTE:** Requires free@home app version >= 2.4.0 diff --git a/docs/img/metadata/internal_error.png b/docs/img/metadata/internal_error.png new file mode 100644 index 0000000000000000000000000000000000000000..9af61f26d7446d8834f2c120230c53e459a11043 GIT binary patch literal 21095 zcmce;Ra_jw67Wj`gy0UrgS)#2cXzkoy0|SQNN{&|776a|?yifwyUQi#oO|!{_wwz- z*6gpRr@O0qdb<8qvmuJ|5{PiPa1am>h*FZG$`BBrr9O_Ezkd1mjk8qpA)uUurBuIu ze7wK@4F33y<1D7>tYUBG>}KR>3Sn+%Z)-~LWa4OQYUgBO?|cr~ApimK9YRV}NYy?4 z1nlOHp@!YLxjs*pCXXhJ+!lc1iH(v^iZ#Os6PXm+hYX;^^dL(i@B#5EP`+?0iVF^yfde==EI< zgvUPDOQLHgN=b;1GmN)-Lv$M*awHo_ z|6`U*B36_B@E{JMW~=$XTBFI#d6Esp|0`kfy0a-%$nxpvp~9E}DVTqZNld-{f31o- zJF~Hxjk;6uSgym?ERBZaum(p)GQ?i}R~K)jd*d0;KLq#Q6-33wS=`Q6NvL)HGqNo~ z!~O9Lu|(Nbd#C+z(~sjo{qp}^wDalqn5ZbG7Jg=_#{5I^z?>x7kIrm;+V0FmiDKq{ z_JMIsVsNZw$JnvTrw-QmSOS7Dpfe>2x$lJ*j=c!^s=reJ>a=rF7z2|l%ZU6W8flKwe1VUMHr#hZXH)LBP3_#F>~DNzrZ z5Qb-!0Il-}vo47;)!N*eoU&k;!-z9Y$x?6fz|p{YxzZfBYYe_jVB!#*7@;?nGgk^Q zE7+RE5&;eM=NwEO%uy{JF51`~@ZA>Kg%0@9g9C=aFlFM()Qn|ZG}#~#r>9Tk}rZK zVk{`jKnzAN?{0;A6g~aG*ByP-8>!~r*bCQM?^<@-<=V}u#4(F+xU#49q*@NSRL|e2 ze)hd|T|pK@ulbv0EqD8Imxi?O?@8lb@?u?GAANfx$dmng`K#pyPiMGccO!i2MXLlV zOghof=hEy%2Z$rNw;C4ZX8fY~fd6-*EL{!#5f2j)8>u(%+hc>CY^_ zP+uyGoIS9xN{9C@&o49-c_TAg{b>V8f6JWLnpC*~u##x{{FLdJI-ZorABO?q$%%6h z$6(|o^)fDa+;Gw%_kaVlxkZ*U$a-~HL!mq=GM!EjN5Yn)B%^i%kHmPYalhdSlr~a! zH&SfwS6#XJ4(33xiwsl6@QAJ??Msv>pZA$9J~0gJl+I!!fHaLmBBsdVN5a_St8bO% z48_~BPdFK6k?u>8W(zT*Reb+;f3NPoSjxs1wi3*MD8e6sr4`6_R8*nx?WVMf#py)l zoZy7jjBY~4Vo4r5T6y|87{(pNQPfEMkgd)_t((iG(SRvIG@Y+=mhS=ATUoNtW-y2M zy0H-tb*zsbZe2skD7lr?REZYjbn58UuXIM#fZ8LYt%uUASi`HlRR!xJaAy`;77+;@ zX^gp#CoXW6)8?23eyqsFS*lHVLBwFA#qKNWjETc%njpyIwWi*c=V{{!Y2l-$Tt}`- z(a~M$NlIsF>FkxNb85g5E7EAV_$QmR{z{KYEbqdTd~5B`6Jqi`)~8XrpSJyKRo5s^t1D2d-hMzC49my3C;G!)81Q)&%-`X6T9oEKj{{XP@BhzSmoH#Rm# zO)M|`2|7G!aSV(Lx~t@*$joP@#nGbN9|6(KB@mgEJDd&byGmpdUv9|@*r{Z4s3cza zIJxN_we}UNfVir?x#rHR=2^M9n^5ldn{&OITrmCzDpcyJ3XCoU*dWko)NizYIIn~=%nBxu z2LDklL4W!!ZF0vpzRo%@^Ac5@E{I}=m@DAV#KcxFzv~;r$vjSwwIrszzPaf_mxBr9E>x$j}Xf7kj>a;=*kYKqeEYy({;+mNCT$mOq%F^MZV=xwrkceQoDN?>5}|SWHU3_u}Ok4%<3>jqhH|~gNgo!#ZC#| zECE;V2kW1);F?DaCstNZxnnNe1BSZ2Di9rTrg( zkJbT4Zw3;|!SCOJuzVwW=axlxH)5cwJPdc?I>V;;(ANWnld~u5H{;K^hBd##v?I!u z^;+91Itq*C4u;jbPPLVDB=)zzgm7T6F!iMYfYqNp-e=i&MJ{d@U7sIn;+R0VQm?L5 zD?Z~ZnHwQp@geA(7^|0mRk#fvsR-TYi_^Wdos8Suanv3u9VT>m^N~7BQ#tEpq_{31 zZ@yV+^z7a#g}kFA91*Tmy@?mQ8u2Ngkm&gGg}V9I*M7RQ<&jOp&dha+xb!9@QfMyW zHX`yC?QR5wpSA?aU~NoeRx9Qxo5nf3d{qjZoX)2T)Uj1^mWt)}@9l+B>_OK-Bjn67j~bj7i&F_Ep;eJ=aX8a?GpS0GVLM}qzK zyDA+!LD<)DFU341JKW^ZYmA8sxArPL8%6BweBu(0tK#jn#i&U8i`=1l?IJrz!xCI2 zuVO8(D%DR2i?coC1(+!5v*bH*qZ}HIYpRtPy?oOjmn&^xMXU-(LU#e~s5|?)X7#`d zF8m@kTnBt;)gm@d^|Mk%%Zjr`+@z8OCEy)4b;v6Kc~H-WTfVy-L)U;N5QmFu(tb(EqpO|3}Bq|)1YV*jab#8+g;6i!9xA9 zE=6jo)(_dXmZZ(tK9FYnI#mSE6<5O6*lYoTX;p^QrXMxmm=s7mZFxEEr0S}>H=06* zK>vIZKf$1WCM|TVlNdWp?32|L+=C;)cCr#R-~LNFOg-i2uN-Hs{hB z#5z0mZ^nw`a$UL#-uU(s5W|$Uu&QohUzg80V75%Ks?-RPL!P@+utn>Z!ok89!56_~ z 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 + errors?: Record; }; From 9683b2d53adbaba4a808c89b78ed2d030a26f265 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Br=C3=A4utigam?= Date: Thu, 25 Jan 2024 08:01:27 +0100 Subject: [PATCH 15/15] 0.34.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f7a7e0a..ec998a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@busch-jaeger/free-at-home", - "version": "0.34.0", + "version": "0.34.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@busch-jaeger/free-at-home", - "version": "0.34.0", + "version": "0.34.1", "license": "ISC", "dependencies": { "@types/ws": "^8.5.7", diff --git a/package.json b/package.json index 4c88ece..aec27a3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@busch-jaeger/free-at-home", - "version": "0.34.0", + "version": "0.34.1", "description": "Library for free@home local device api", "repository": { "type": "git",