From 4fb0267ad43303aecb68a8e066e16a5998a605df Mon Sep 17 00:00:00 2001 From: swevictor Date: Thu, 6 Oct 2022 09:35:15 +0200 Subject: [PATCH 1/5] Catch errors due to unknown device type to avoid addon crashing --- plejd/PlejdApi.js | 98 +++++++++++++++++++++++++++-------------------- plejd/config.json | 2 +- 2 files changed, 58 insertions(+), 42 deletions(-) diff --git a/plejd/PlejdApi.js b/plejd/PlejdApi.js index 919755e..1b511fa 100644 --- a/plejd/PlejdApi.js +++ b/plejd/PlejdApi.js @@ -428,34 +428,43 @@ class PlejdApi { const dimmable = device.traits === TRAITS.DIMMABLE; // dimmable = settings.dimCurve !== 'NonDimmable'; - const { name: typeName, type: deviceType } = this._getDeviceType(plejdDevice); - let loadType = deviceType; - if (device.outputType === 'RELAY') { - loadType = 'switch'; - } else if (device.outputType === 'LIGHT') { - loadType = 'light'; + try { + const { name: typeName, type: deviceType } = this._getDeviceType(plejdDevice); + + let loadType = deviceType; + if (device.outputType === 'RELAY') { + loadType = 'switch'; + } else if (device.outputType === 'LIGHT') { + loadType = 'light'; + } + + const room = this.siteDetails.rooms.find((x) => x.roomId === device.roomId); + const roomTitle = room ? room.title : undefined; + + /** @type {import('types/DeviceRegistry').OutputDevice} */ + const outputDevice = { + bleOutputAddress, + deviceId: device.deviceId, + dimmable, + name: device.title, + output: deviceOutput, + roomId: device.roomId, + roomName: roomTitle, + state: undefined, + type: loadType, + typeName, + version: plejdDevice.firmware.version, + uniqueId: uniqueOutputId, + }; + + this.deviceRegistry.addOutputDevice(outputDevice); + + } catch (error) { + logger.error(`Error trying to create output device: ${error}`); + logger.warn(`device (from API response) when error happened: ${JSON.stringify(device, null, 2)}`); + logger.warn(`plejdDevice (from API response) when error happened: ${JSON.stringify(plejdDevice, null, 2)}`); } - const room = this.siteDetails.rooms.find((x) => x.roomId === device.roomId); - const roomTitle = room ? room.title : undefined; - - /** @type {import('types/DeviceRegistry').OutputDevice} */ - const outputDevice = { - bleOutputAddress, - deviceId: device.deviceId, - dimmable, - name: device.title, - output: deviceOutput, - roomId: device.roomId, - roomName: roomTitle, - state: undefined, - type: loadType, - typeName, - version: plejdDevice.firmware.version, - uniqueId: uniqueOutputId, - }; - - this.deviceRegistry.addOutputDevice(outputDevice); } } else { // The device does not have an output. It can be assumed to be a WPH-01 or a WRT-01 @@ -476,21 +485,28 @@ class PlejdApi { ); const uniqueInputId = this.deviceRegistry.getUniqueInputId(device.deviceId, input.input); - const { name: typeName, type, broadcastClicks } = this._getDeviceType(plejdDevice); - if (broadcastClicks) { - /** @type {import('types/DeviceRegistry').InputDevice} */ - const inputDevice = { - bleInputAddress, - deviceId: device.deviceId, - name: device.title, - input: input.input, - roomId: device.roomId, - type, - typeName, - version: plejdDevice.firmware.version, - uniqueId: uniqueInputId, - }; - this.deviceRegistry.addInputDevice(inputDevice); + + try { + const { name: typeName, type, broadcastClicks } = this._getDeviceType(plejdDevice); + if (broadcastClicks) { + /** @type {import('types/DeviceRegistry').InputDevice} */ + const inputDevice = { + bleInputAddress, + deviceId: device.deviceId, + name: device.title, + input: input.input, + roomId: device.roomId, + type, + typeName, + version: plejdDevice.firmware.version, + uniqueId: uniqueInputId, + }; + this.deviceRegistry.addInputDevice(inputDevice); + } + } catch (error) { + logger.error(`Error trying to create input device: ${error}`); + logger.warn(`device (from API response) when error happened: ${JSON.stringify(device, null, 2)}`); + logger.warn(`plejdDevice (from API response) when error happened: ${JSON.stringify(plejdDevice, null, 2)}`); } }); } diff --git a/plejd/config.json b/plejd/config.json index 466b25b..9e8eb41 100644 --- a/plejd/config.json +++ b/plejd/config.json @@ -1,6 +1,6 @@ { "name": "Plejd", - "version": "0.9.1", + "version": "0.10.0-alpha", "slug": "plejd", "description": "Adds support for the Swedish home automation devices from Plejd.", "url": "https://github.com/icanos/hassio-plejd/", From 9561a0586d129b2b646aeda9755c05f5355cee9b Mon Sep 17 00:00:00 2001 From: swevictor Date: Sun, 16 Oct 2022 09:41:48 +0200 Subject: [PATCH 2/5] Improve documentation - clarify that username is normally email address --- plejd/Details.md | 4 ++-- plejd/README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plejd/Details.md b/plejd/Details.md index ee49bfe..b6aefe8 100644 --- a/plejd/Details.md +++ b/plejd/Details.md @@ -19,7 +19,7 @@ And `Configuration Parameters` on the same page. The only parameters needing a value are - site -- username +- username (typically email address) - password - mqttUsername e.g. `mqtt-api-user` - mqttPassword @@ -28,7 +28,7 @@ Now you can start the Plejd add-on ## Where are the lights? -Head over to [Configuration -> Integrations](http://homeassistant.local:8123/config/integrations) and click Configure on MQTT +Head over to [Configuration -> Integrations](http://homeassistant.local:8123/config/integrations) and click Configure on MQTT After this step a new `Mosquito broker` should appear on the same page. If everything was setup correctly. It will list your lights under `1 entity`/`n entities` diff --git a/plejd/README.md b/plejd/README.md index e48b1c3..85cf3d4 100644 --- a/plejd/README.md +++ b/plejd/README.md @@ -117,7 +117,7 @@ The plugin needs you to configure some settings before working. You find these o | Parameter | Value | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | site | Name of your Plejd site, the name is displayed in the Plejd app (top bar). | -| username | Username of your Plejd account, this is used to fetch the crypto key and devices from the Plejd API. | +| username | Email/username of your Plejd account, this is used to fetch the crypto key and devices from the Plejd API. | | password | Password of your Plejd account, this is used to fetch the crypto key and devices from the Plejd API. | | mqttBroker | URL of the MQTT Broker, eg. mqtt:// | | mqttUsername | Username of the MQTT broker | From 0ed2ad849cafd4bce868aba8f44cd03d185eabd4 Mon Sep 17 00:00:00 2001 From: swevictor Date: Sun, 16 Oct 2022 14:35:43 +0200 Subject: [PATCH 3/5] Update the device list with new devices - Based on the device list from @SM6WGG in https://github.com/icanos/hassio-plejd/issues/250#issuecomment-1269535099 --- plejd/PlejdApi.js | 92 +++++++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 35 deletions(-) diff --git a/plejd/PlejdApi.js b/plejd/PlejdApi.js index 1b511fa..c0887e4 100644 --- a/plejd/PlejdApi.js +++ b/plejd/PlejdApi.js @@ -250,10 +250,9 @@ class PlejdApi { switch (parseInt(plejdDevice.hardwareId, 10)) { case 1: - case 11: - case 14: return { name: 'DIM-01', + description: '1-channel dimmer LED, 300 VA', type: 'light', dimmable: true, broadcastClicks: false, @@ -261,6 +260,7 @@ class PlejdApi { case 2: return { name: 'DIM-02', + description: '2-channel dimmer LED, 2*100 VA', type: 'light', dimmable: true, broadcastClicks: false, @@ -268,20 +268,24 @@ class PlejdApi { case 3: return { name: 'CTR-01', + description: '1-channel relay with 0-10V output, 3500 VA', type: 'light', dimmable: false, broadcastClicks: false, }; - case 4: - return { - name: 'GWY-01', - type: 'sensor', - dimmable: false, - broadcastClicks: false, - }; + // Gateway doesn't show up in devices list in API response + // case 4: + // return { + // name: 'GWY-01', + // description: 'Gateway to enable control via internet and integrations', + // type: 'sensor', + // dimmable: false, + // broadcastClicks: false, + // }; case 5: return { name: 'LED-10', + description: '1-channel LED dimmer/driver, 10 W', type: 'light', dimmable: true, broadcastClicks: false, @@ -289,60 +293,75 @@ class PlejdApi { case 6: return { name: 'WPH-01', + description: 'Wireless push button, 4 buttons. 2 channels, on and off buttons for each channel', type: 'device_automation', dimmable: false, broadcastClicks: true, }; case 7: + // Unknown, pre-release (?) version, kept for backwards compatibility. See https://github.com/icanos/hassio-plejd/issues/250 return { name: 'REL-01', + description: '1 channel relay, 3500 VA', type: 'switch', dimmable: false, broadcastClicks: false, }; case 8: - case 9: - // Unknown return { - name: '-unknown-', - type: 'light', + name: 'SPR-01', + description: 'Smart plug on/off with relay, 3500 VA', + type: 'switch', dimmable: false, broadcastClicks: false, }; case 10: return { name: 'WRT-01', + description: 'Wireless rotary button', type: 'device_automation', dimmable: false, broadcastClicks: true, }; - case 12: - // Unknown + case 11: return { - name: '-unknown-', + name: 'DIM-01-2P', + description: '1-channel dimmer LED with 2-pole breaking, 300 VA', type: 'light', - dimmable: false, + dimmable: true, broadcastClicks: false, }; - case 13: + // DAL-01 is presumably a very special device + // Please open a new issue if you have ideas on how to handel + // Below could be use as testing, but since one device can have up to 64 slaves it probably won't work + // case 12: + // return { + // name: 'DAL-01', + // description: 'Dali broadcast with dimmer and tuneable white support', + // type: 'light', + // dimmable: true, + // broadcastClicks: false, + // }; + case 14: return { - name: 'Generic', + name: 'DIM-01', + description: '1-channel dimmer LED, 300 VA ("LC" hardware/chip version)', type: 'light', - dimmable: false, + dimmable: true, broadcastClicks: false, }; case 15: - case 16: - // Unknown return { - name: '-unknown-', + name: 'DIM-02', + description: '2-channel dimmer LED, 2*100 VA ("LC" hardware/chip version)', type: 'light', - dimmable: false, + dimmable: true, broadcastClicks: false, }; case 17: return { - name: 'REL-01', + name: 'REL-01-2P', + description: '1-channel relay with 2-pole 3500 VA', type: 'switch', dimmable: false, broadcastClicks: false, @@ -350,26 +369,29 @@ class PlejdApi { case 18: return { name: 'REL-02', + description: '2-channel relay with combined 3500 VA', type: 'switch', dimmable: false, broadcastClicks: false, }; - case 19: - // Unknown - return { - name: '-unknown-', - type: 'light', - dimmable: false, - broadcastClicks: false, - }; case 20: return { + // Unknown, pre-release (?) version, kept for backwards compatibility. See https://github.com/icanos/hassio-plejd/issues/250 name: 'SPR-01', - type: 'switch', + description: 'Smart plug on/off with relay, 3500 VA', + type: 'device_automation', dimmable: false, broadcastClicks: false, }; - default: + case 36: + return { + name: 'LED-75', + description: '1-channel LED dimmer/driver with tuneable white, 10 W', + type: 'light', + dimmable: true, + broadcastClicks: false, + }; + default: throw new Error(`Unknown device type with id ${plejdDevice.hardwareId}`); } } From d9d4f9bac8d7b4bda04ceaf2706812bd882034f7 Mon Sep 17 00:00:00 2001 From: swevictor Date: Sun, 16 Oct 2022 14:36:40 +0200 Subject: [PATCH 4/5] Change API entity definition from enum to string for relevant types --- plejd/types/ApiSite.d.ts | 68 +++++++--------------------------------- 1 file changed, 12 insertions(+), 56 deletions(-) diff --git a/plejd/types/ApiSite.d.ts b/plejd/types/ApiSite.d.ts index 4a6e562..db0be07 100644 --- a/plejd/types/ApiSite.d.ts +++ b/plejd/types/ApiSite.d.ts @@ -30,7 +30,7 @@ export interface ApiSite { outputGroups: { [key: string]: OutputGroup }; roomAddress: { [key: string]: number }; sceneIndex: { [key: string]: number }; - images: Images; + images: string; deviceLimit: number; } @@ -106,7 +106,7 @@ export interface Hardware { predefinedLoad: PredefinedLoad; supportedFirmware: PredefinedLoad; ACL: AstroEventACL; - objectId: HardwareObjectID; + objectId: string; __type: AstroEventType; className: HardwareClassName; } @@ -132,7 +132,7 @@ export enum ImageType { export interface PlejdMeshClass { __type: InstallerType; className: SiteClassName; - objectId: ObjectID; + objectId: string; } export enum InstallerType { @@ -148,28 +148,11 @@ export enum SiteClassName { UserProfile = 'UserProfile', } -export enum ObjectID { - BBBJO2Cufm = 'BBBJO2cufm', - D4Dw87Hq21 = 'D4DW87HQ21', - FCrrS1NJHH = 'FCrrS1nJHH', - GX1W4P06QS = 'gX1W4p06QS', - Ndlvzgh4Df = 'ndlvzgh4df', - UHoKQLuXqZ = 'uHoKQLuXqZ', - VfHiawBPA8 = 'vfHiawBPA8', - WgAFPloWjK = 'wgAfPloWjK', - YkyNDotBNa = 'YkyNDotBNa', -} - export enum Name { Ctr01 = 'CTR-01', Dim01 = 'DIM-01', } -export enum HardwareObjectID { - R3Gfd6ACAu = 'R3gfd6ACAu', - XjslOltgvi = 'xjslOltgvi', -} - export interface PredefinedLoad { __type: SupportedFirmwareType; className: PredefinedLoadClassName; @@ -204,7 +187,7 @@ export interface Gateway { deviceId: string; siteId: string; hardwareId: string; - installer: ObjectID; + installer: string; firmware: number; firmwareObject: Firmware; dirtyInstall: boolean; @@ -220,47 +203,20 @@ export interface Gateway { } export interface Firmware { - notes: Notes; + notes: string; createdAt: Date; updatedAt: Date; data: Image; metaData: Image; - version: Version; + version: string; buildTime: number; firmwareApi: string; ACL: AstroEventACL; - objectId: FirmwareObjectObjectID; + objectId: string; __type: AstroEventType; className: SiteClassName; } -export enum Notes { - Ctr01 = 'CTR-01', - Ctr20ReleaseCandidate1 = 'Ctr 2.0 Release candidate 1', - Dim20ReleaseCandidate1 = 'Dim 2.0 Release candidate 1', - Dim221ReleaseCandidate = 'Dim 2.2.1 Release Candidate', - GWY10ReleaseCandidate = 'GWY 1.0 Release Candidate', -} - -export enum FirmwareObjectObjectID { - BBBJO2Cufm = 'BBBJO2cufm', - E6YxfREDuF = 'E6yxfREDuF', - JYSZ0EvyCU = 'JYSZ0EvyCU', - Ndlvzgh4Df = 'ndlvzgh4df', - RlglTfVHDe = 'rlglTfVHDe', -} - -export enum Version { - The12 = '1.2', - The20 = '2.0', - The221 = '2.2.1', - The304 = '3.0.4', -} - -export interface Images { - '2afc6c6e-7a26-466a-b8ec-febbca90f5f7': string; -} - export interface InputSetting { deviceId: string; input: number; @@ -472,7 +428,7 @@ export interface PlejdMesh { updatedAt: Date; site: PlejdMeshClass; ACL: AstroEventACL; - objectId: ObjectID; + objectId: string; __type: AstroEventType; className: SiteClassName; } @@ -549,7 +505,7 @@ export interface Scene { } export interface SiteDetailsSite { - installers: ObjectID[]; + installers: string[]; title: string; siteId: string; version: number; @@ -564,7 +520,7 @@ export interface SiteDetailsSite { country: string; previousOwners: string[]; ACL: AstroEventACL; - objectId: ObjectID; + objectId: string; __type: AstroEventType; className: SiteClassName; } @@ -581,7 +537,7 @@ export interface DeviceAstroTable { export interface SitePermission { siteId: string; - userId: ObjectID; + userId: string; user: User; isOwner: boolean; isInstaller: boolean; @@ -608,7 +564,7 @@ export interface User { _failed_login_count: number; hasIntegration: boolean; ACL: UserACL; - objectId: ObjectID; + objectId: string; __type: AstroEventType; className: SiteClassName; } From a03f2556d677aadb5ab4cbe7b4c39fe1c61b812c Mon Sep 17 00:00:00 2001 From: swevictor Date: Sun, 16 Oct 2022 14:40:15 +0200 Subject: [PATCH 5/5] Add description to device types to improve clarity --- plejd/PlejdApi.js | 19 ++++++++++++------- plejd/types/DeviceRegistry.d.ts | 2 ++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/plejd/PlejdApi.js b/plejd/PlejdApi.js index c0887e4..4c847f8 100644 --- a/plejd/PlejdApi.js +++ b/plejd/PlejdApi.js @@ -451,9 +451,9 @@ class PlejdApi { // dimmable = settings.dimCurve !== 'NonDimmable'; try { - const { name: typeName, type: deviceType } = this._getDeviceType(plejdDevice); + const decodedDeviceType = this._getDeviceType(plejdDevice); - let loadType = deviceType; + let loadType = decodedDeviceType.type; if (device.outputType === 'RELAY') { loadType = 'switch'; } else if (device.outputType === 'LIGHT') { @@ -474,7 +474,8 @@ class PlejdApi { roomName: roomTitle, state: undefined, type: loadType, - typeName, + typeDescription: decodedDeviceType.description, + typeName: decodedDeviceType.name, version: plejdDevice.firmware.version, uniqueId: uniqueOutputId, }; @@ -509,8 +510,9 @@ class PlejdApi { const uniqueInputId = this.deviceRegistry.getUniqueInputId(device.deviceId, input.input); try { - const { name: typeName, type, broadcastClicks } = this._getDeviceType(plejdDevice); - if (broadcastClicks) { + const decodedDeviceType = this._getDeviceType(plejdDevice); + + if (decodedDeviceType.broadcastClicks) { /** @type {import('types/DeviceRegistry').InputDevice} */ const inputDevice = { bleInputAddress, @@ -518,8 +520,9 @@ class PlejdApi { name: device.title, input: input.input, roomId: device.roomId, - type, - typeName, + type: decodedDeviceType.type, + typeDescription: decodedDeviceType.description, + typeName: decodedDeviceType.name, version: plejdDevice.firmware.version, uniqueId: uniqueInputId, }; @@ -560,6 +563,7 @@ class PlejdApi { roomName: undefined, state: undefined, type: 'light', + typeDescription: 'A Plejd room', typeName: 'Room', uniqueId: roomId, version: undefined, @@ -589,6 +593,7 @@ class PlejdApi { roomName: undefined, state: false, type: 'scene', + typeDescription: 'A Plejd scene', typeName: 'Scene', version: undefined, uniqueId: scene.sceneId, diff --git a/plejd/types/DeviceRegistry.d.ts b/plejd/types/DeviceRegistry.d.ts index 7a86661..5eba5c8 100644 --- a/plejd/types/DeviceRegistry.d.ts +++ b/plejd/types/DeviceRegistry.d.ts @@ -13,6 +13,7 @@ export interface OutputDevice { roomName: string | undefined; state: boolean | undefined; type: string; + typeDescription: string; typeName: string; version: string; uniqueId: string; @@ -27,6 +28,7 @@ export interface InputDevice { input: number; roomId: string; type: string; + typeDescription: string; typeName: string; version: string; uniqueId: string;