diff --git a/README.md b/README.md index 27cfb883..dd4a7b84 100644 --- a/README.md +++ b/README.md @@ -239,7 +239,12 @@ TBD ## Changelog ### **WORK IN PROGRESS** -* (@GermanBluefox) Showed the device name in paring dialog +* (@GermanBluefox) Showed the device name in paring dialog +* (@GermanBluefox/Apollon77) Adjusts connection type icons +* (@Apollon77) Optimized the discovery dialog handling +* (@Apollon77) Fixed Thermostat for Controller to update temperatures +* (@Apollon77) Gives Energy sensors a dedicated icon +* (@Apollon77) Optimized an fixed multiple things ### 0.3.6 (2025-01-13) * (@GermanBluefox) Fixed GUI errors diff --git a/package-lock.json b/package-lock.json index fff4fa19..56d0b8a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,9 +13,9 @@ "@iobroker/dm-utils": "^1.0.6", "@iobroker/i18n": "^0.3.1", "@iobroker/type-detector": "^4.1.1", - "@matter/main": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/nodejs": "0.12.0-alpha.0-20250113-e619723a5", - "@project-chip/matter.js": "0.12.0-alpha.0-20250113-e619723a5", + "@matter/main": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/nodejs": "0.12.0-alpha.0-20250113-1c80c6d58", + "@project-chip/matter.js": "0.12.0-alpha.0-20250113-1c80c6d58", "axios": "^1.7.9", "jsonwebtoken": "^9.0.2" }, @@ -40,7 +40,7 @@ "node": ">=18" }, "optionalDependencies": { - "@matter/nodejs-ble": "0.12.0-alpha.0-20250113-e619723a5" + "@matter/nodejs-ble": "0.12.0-alpha.0-20250113-1c80c6d58" } }, "node_modules/@alcalzone/pak": { @@ -1001,64 +1001,64 @@ } }, "node_modules/@matter/general": { - "version": "0.12.0-alpha.0-20250113-e619723a5", - "resolved": "https://registry.npmjs.org/@matter/general/-/general-0.12.0-alpha.0-20250113-e619723a5.tgz", - "integrity": "sha512-Uf9BPrvVFBVW/JhpJ5PJeVNFIJ+eVBJUJY5px86PBANB3m5V+WEwjLyIySBOWDtPivxM7RDF3vQdDA2S3Dv3cw==", + "version": "0.12.0-alpha.0-20250113-1c80c6d58", + "resolved": "https://registry.npmjs.org/@matter/general/-/general-0.12.0-alpha.0-20250113-1c80c6d58.tgz", + "integrity": "sha512-v3bAbfbHZKpF8I9v5SXgLGcwaANI9xYIHkEuCdliPRum8+wxB756DuS6Y8IAKi7mZW1h1aisOl7SJrw143LiNA==", "license": "Apache-2.0", "dependencies": { "@noble/curves": "^1.8.0" } }, "node_modules/@matter/main": { - "version": "0.12.0-alpha.0-20250113-e619723a5", - "resolved": "https://registry.npmjs.org/@matter/main/-/main-0.12.0-alpha.0-20250113-e619723a5.tgz", - "integrity": "sha512-yd3A3xt4SawUk7H0QI7MGC7yBLN4jSywe8TS3QfVYplRelzrlKELA4qnTiFyORWYzoWXLdLO+hGyIdfLLpU18w==", + "version": "0.12.0-alpha.0-20250113-1c80c6d58", + "resolved": "https://registry.npmjs.org/@matter/main/-/main-0.12.0-alpha.0-20250113-1c80c6d58.tgz", + "integrity": "sha512-gc4La9UD5iPm3/f0xS7wmfNu7lf8sHiBdliXsuGXAbJgu0smB3rljbJTk+czQpRR0IY7GmPQ0Tb8J5Nh364l0g==", "license": "Apache-2.0", "dependencies": { - "@matter/general": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/model": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/node": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/protocol": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/types": "0.12.0-alpha.0-20250113-e619723a5", + "@matter/general": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/model": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/node": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/protocol": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/types": "0.12.0-alpha.0-20250113-1c80c6d58", "@noble/curves": "^1.7.0" }, "optionalDependencies": { - "@matter/nodejs": "0.12.0-alpha.0-20250113-e619723a5" + "@matter/nodejs": "0.12.0-alpha.0-20250113-1c80c6d58" } }, "node_modules/@matter/model": { - "version": "0.12.0-alpha.0-20250113-e619723a5", - "resolved": "https://registry.npmjs.org/@matter/model/-/model-0.12.0-alpha.0-20250113-e619723a5.tgz", - "integrity": "sha512-abpzEY4MeiQSaNss07aC6Bb9ajoRN7/V/tO6IHSd8Hnq22GUal51MYb5EnOT//paDIfOIzIJdh+Szgjq607oTA==", + "version": "0.12.0-alpha.0-20250113-1c80c6d58", + "resolved": "https://registry.npmjs.org/@matter/model/-/model-0.12.0-alpha.0-20250113-1c80c6d58.tgz", + "integrity": "sha512-X8YY8u/nlOH8X4nY3YAMlbj1QUaK4oS/Kgh/AIDtfDxLFgmVNSDBZmH1lz/z2LgKEckAlWQ2Mkv6nz9222jSsQ==", "license": "Apache-2.0", "dependencies": { - "@matter/general": "0.12.0-alpha.0-20250113-e619723a5", + "@matter/general": "0.12.0-alpha.0-20250113-1c80c6d58", "@noble/curves": "^1.7.0" } }, "node_modules/@matter/node": { - "version": "0.12.0-alpha.0-20250113-e619723a5", - "resolved": "https://registry.npmjs.org/@matter/node/-/node-0.12.0-alpha.0-20250113-e619723a5.tgz", - "integrity": "sha512-IajuC/xUol+wAGC3cuNwn2s6lrvx80bIiTPzY/I7wsyv33N1LuA8fvlYM/Fu3yEPdaCULjnezwVaQjRPIPt7eA==", + "version": "0.12.0-alpha.0-20250113-1c80c6d58", + "resolved": "https://registry.npmjs.org/@matter/node/-/node-0.12.0-alpha.0-20250113-1c80c6d58.tgz", + "integrity": "sha512-xqg7FKoZ3AJqGb6juE4gYumu77USbmpGUtO2RfvI/Ks9IkbTgNtaCYj8aHslegzmMEOKlgvX8hDtLWww8yKJMg==", "license": "Apache-2.0", "dependencies": { - "@matter/general": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/model": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/protocol": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/types": "0.12.0-alpha.0-20250113-e619723a5", + "@matter/general": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/model": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/protocol": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/types": "0.12.0-alpha.0-20250113-1c80c6d58", "@noble/curves": "^1.7.0" } }, "node_modules/@matter/nodejs": { - "version": "0.12.0-alpha.0-20250113-e619723a5", - "resolved": "https://registry.npmjs.org/@matter/nodejs/-/nodejs-0.12.0-alpha.0-20250113-e619723a5.tgz", - "integrity": "sha512-SkYeg1QmZkTlBTpn/iktu+RGrAf5oKlIUWvmGqmYnx8B1z0ZVFHjqJpBUYY+efvrELWYiAsX5kT3laYtf5+OwA==", + "version": "0.12.0-alpha.0-20250113-1c80c6d58", + "resolved": "https://registry.npmjs.org/@matter/nodejs/-/nodejs-0.12.0-alpha.0-20250113-1c80c6d58.tgz", + "integrity": "sha512-qC97T9L7eH32O40RIbLgBPKGuIcbm3bEjbAqzccxvtQGOhTQXvypfdT6yONXjAi82KBDgurhmILerPNPIoWWvw==", "license": "Apache-2.0", "dependencies": { - "@matter/general": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/node": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/protocol": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/types": "0.12.0-alpha.0-20250113-e619723a5", + "@matter/general": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/node": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/protocol": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/types": "0.12.0-alpha.0-20250113-1c80c6d58", "node-localstorage": "^3.0.5" }, "engines": { @@ -1066,15 +1066,15 @@ } }, "node_modules/@matter/nodejs-ble": { - "version": "0.12.0-alpha.0-20250113-e619723a5", - "resolved": "https://registry.npmjs.org/@matter/nodejs-ble/-/nodejs-ble-0.12.0-alpha.0-20250113-e619723a5.tgz", - "integrity": "sha512-fcpY/LhCFUc/FNrLnDPxuEnH1DgQBQ0qguwkzQwnKSMwUXUzVqRxtPjJMbnHQrQFNkYVXB1YDJPkY6RlOGWUvA==", + "version": "0.12.0-alpha.0-20250113-1c80c6d58", + "resolved": "https://registry.npmjs.org/@matter/nodejs-ble/-/nodejs-ble-0.12.0-alpha.0-20250113-1c80c6d58.tgz", + "integrity": "sha512-fZmJEoaWrs+swB8aXfn880L07376CKO5ApNYtWbN4HauzEZr7G1rkQdqf7tsvogdBv/REy5TIVv7Qi10HrqVFg==", "license": "Apache-2.0", "optional": true, "dependencies": { - "@matter/general": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/protocol": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/types": "0.12.0-alpha.0-20250113-e619723a5" + "@matter/general": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/protocol": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/types": "0.12.0-alpha.0-20250113-1c80c6d58" }, "engines": { "node": ">=18.0.0" @@ -1085,25 +1085,25 @@ } }, "node_modules/@matter/protocol": { - "version": "0.12.0-alpha.0-20250113-e619723a5", - "resolved": "https://registry.npmjs.org/@matter/protocol/-/protocol-0.12.0-alpha.0-20250113-e619723a5.tgz", - "integrity": "sha512-Pe0DjrBRPuXqzncEJi/ajldUDqd+n2svBsO/eqwH99gtRbmU4c1NO81nwdvOMpVNScqXaT8G1k1zhU8kGrR+xQ==", + "version": "0.12.0-alpha.0-20250113-1c80c6d58", + "resolved": "https://registry.npmjs.org/@matter/protocol/-/protocol-0.12.0-alpha.0-20250113-1c80c6d58.tgz", + "integrity": "sha512-qihNQoZ2RSMBs9uLFCbkKjDEiUUOi05k28OsqLbnI5AOO4VoS8AtM3pYqV0nP2+x+aPKrCQ9h0DWF+w3INjjFQ==", "license": "Apache-2.0", "dependencies": { - "@matter/general": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/model": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/types": "0.12.0-alpha.0-20250113-e619723a5", + "@matter/general": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/model": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/types": "0.12.0-alpha.0-20250113-1c80c6d58", "@noble/curves": "^1.8.0" } }, "node_modules/@matter/types": { - "version": "0.12.0-alpha.0-20250113-e619723a5", - "resolved": "https://registry.npmjs.org/@matter/types/-/types-0.12.0-alpha.0-20250113-e619723a5.tgz", - "integrity": "sha512-XK2YxUczg4eoddj7b5mct3NiCi5s5JnWpzTjYnrj5vG5kUkGX38LxO0MZx7ztbaOO/SMHYFQLsBsTlsJ+6BI+Q==", + "version": "0.12.0-alpha.0-20250113-1c80c6d58", + "resolved": "https://registry.npmjs.org/@matter/types/-/types-0.12.0-alpha.0-20250113-1c80c6d58.tgz", + "integrity": "sha512-OPXCrcXYEBRiEHO6iuxuolWznQvNcDpJQL2nyDE+qUlBJSNsa3CQpHDpyJPg5oImyFTHCIesgXku4tNtqMfqzA==", "license": "Apache-2.0", "dependencies": { - "@matter/general": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/model": "0.12.0-alpha.0-20250113-e619723a5", + "@matter/general": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/model": "0.12.0-alpha.0-20250113-1c80c6d58", "@noble/curves": "^1.7.0" } }, @@ -1266,16 +1266,16 @@ } }, "node_modules/@project-chip/matter.js": { - "version": "0.12.0-alpha.0-20250113-e619723a5", - "resolved": "https://registry.npmjs.org/@project-chip/matter.js/-/matter.js-0.12.0-alpha.0-20250113-e619723a5.tgz", - "integrity": "sha512-njJjP1G6SwNLHTxWN7GR2wsS3SIOjMYR5P8hUvl7gCOorI8OC9LTraW7O738aHmN37pZjCYFtJW76oyQD/iV8Q==", + "version": "0.12.0-alpha.0-20250113-1c80c6d58", + "resolved": "https://registry.npmjs.org/@project-chip/matter.js/-/matter.js-0.12.0-alpha.0-20250113-1c80c6d58.tgz", + "integrity": "sha512-MhT/PEGmFSTnEMzb58P7KeOrawpJFmYbVmrkDr2FCo3rvq6XGLeWOo78YaMEElrxLbm6uuabr22WikafmIuzxg==", "license": "Apache-2.0", "dependencies": { - "@matter/general": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/model": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/node": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/protocol": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/types": "0.12.0-alpha.0-20250113-e619723a5", + "@matter/general": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/model": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/node": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/protocol": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/types": "0.12.0-alpha.0-20250113-1c80c6d58", "@noble/curves": "^1.7.0" } }, diff --git a/package.json b/package.json index 7621267d..5ad09038 100644 --- a/package.json +++ b/package.json @@ -23,16 +23,16 @@ "url": "https://github.com/ioBroker/ioBroker.matter" }, "optionalDependencies": { - "@matter/nodejs-ble": "0.12.0-alpha.0-20250113-e619723a5" + "@matter/nodejs-ble": "0.12.0-alpha.0-20250113-1c80c6d58" }, "dependencies": { "@iobroker/adapter-core": "^3.2.3", "@iobroker/i18n": "^0.3.1", "@iobroker/dm-utils": "^1.0.6", "@iobroker/type-detector": "^4.1.1", - "@matter/main": "0.12.0-alpha.0-20250113-e619723a5", - "@matter/nodejs": "0.12.0-alpha.0-20250113-e619723a5", - "@project-chip/matter.js": "0.12.0-alpha.0-20250113-e619723a5", + "@matter/main": "0.12.0-alpha.0-20250113-1c80c6d58", + "@matter/nodejs": "0.12.0-alpha.0-20250113-1c80c6d58", + "@project-chip/matter.js": "0.12.0-alpha.0-20250113-1c80c6d58", "axios": "^1.7.9", "jsonwebtoken": "^9.0.2" }, diff --git a/src-admin/package-lock.json b/src-admin/package-lock.json index 6ba6d961..136476df 100644 --- a/src-admin/package-lock.json +++ b/src-admin/package-lock.json @@ -9,8 +9,8 @@ "version": "0.3.6", "dependencies": { "@foxriver76/iob-component-lib": "^0.2.0", - "@iobroker/adapter-react-v5": "^7.4.12", - "@iobroker/dm-gui-components": "^7.4.12", + "@iobroker/adapter-react-v5": "^7.4.14", + "@iobroker/dm-gui-components": "^7.4.14", "@iobroker/type-detector": "^4.1.1", "@types/react-dom": "^18.3.5", "@types/uuid": "^10.0.0", @@ -988,9 +988,9 @@ } }, "node_modules/@iobroker/adapter-react-v5": { - "version": "7.4.12", - "resolved": "https://registry.npmjs.org/@iobroker/adapter-react-v5/-/adapter-react-v5-7.4.12.tgz", - "integrity": "sha512-HoZgmqHDRZPGx5MFnrTnOieXxMNP3RKHe9MWA4dcEvv/SqXqc6Zc66stYU1TgYpirys/1jNZgfwUV8uECaPzzQ==", + "version": "7.4.14", + "resolved": "https://registry.npmjs.org/@iobroker/adapter-react-v5/-/adapter-react-v5-7.4.14.tgz", + "integrity": "sha512-4iDWeUVRf10ofBn+QqaIoPZQqd9vadumKIZdwBgSrQvdyrawk9Q+lf6BLkt0+Ln//rqfbVZG51CJrz47ZP+Xmg==", "license": "MIT", "dependencies": { "@emotion/react": "^11.13.5", @@ -1119,13 +1119,13 @@ } }, "node_modules/@iobroker/dm-gui-components": { - "version": "7.4.12", - "resolved": "https://registry.npmjs.org/@iobroker/dm-gui-components/-/dm-gui-components-7.4.12.tgz", - "integrity": "sha512-9a/61q7t2t1rzjZldx4hvzqkamy//zgCS3MuqVHGZHGwaeWKQf8ItPXzD7vbIj9HnmDqDx7pQw3POuoYfud1mA==", + "version": "7.4.14", + "resolved": "https://registry.npmjs.org/@iobroker/dm-gui-components/-/dm-gui-components-7.4.14.tgz", + "integrity": "sha512-RTGfx5BlpzkibkJxxZKTSCk71nsO96Iwf1B1Se0SgQ1F1l/bALDwCgiPVXRY2p37KhG1j/EtDPCicTlQOGtZ1g==", "license": "MIT", "dependencies": { - "@iobroker/adapter-react-v5": "7.4.12", - "@iobroker/json-config": "7.4.12" + "@iobroker/adapter-react-v5": "7.4.14", + "@iobroker/json-config": "7.4.14" } }, "node_modules/@iobroker/js-controller-common": { @@ -1185,11 +1185,11 @@ } }, "node_modules/@iobroker/json-config": { - "version": "7.4.12", - "resolved": "https://registry.npmjs.org/@iobroker/json-config/-/json-config-7.4.12.tgz", - "integrity": "sha512-V23H98RLv18OArJtSa1dXqqMyydfU6dJBI8ucBac64fJUh6Jwmsv6Iv0IJx7stW0qM25w/qe3OnuaMxZqOtbfw==", + "version": "7.4.14", + "resolved": "https://registry.npmjs.org/@iobroker/json-config/-/json-config-7.4.14.tgz", + "integrity": "sha512-VT58k0NzriZZD21FDUXU3IYRyu/lfd3wCdeEpEMyf0eGr6+WQU5KUnBeCWLDU6m8BgGsqatJAmMtz31Je8od+w==", "dependencies": { - "@iobroker/adapter-react-v5": "7.4.12", + "@iobroker/adapter-react-v5": "7.4.14", "@mui/x-date-pickers": "^7.23.0", "crypto-js": "^4.2.0", "react-ace": "^13.0.0", diff --git a/src-admin/package.json b/src-admin/package.json index 75c2bca3..bbe8c8ae 100644 --- a/src-admin/package.json +++ b/src-admin/package.json @@ -7,8 +7,8 @@ }, "dependencies": { "@foxriver76/iob-component-lib": "^0.2.0", - "@iobroker/adapter-react-v5": "^7.4.12", - "@iobroker/dm-gui-components": "^7.4.12", + "@iobroker/adapter-react-v5": "^7.4.14", + "@iobroker/dm-gui-components": "^7.4.14", "@iobroker/type-detector": "^4.1.1", "@types/react-dom": "^18.3.5", "@types/uuid": "^10.0.0", @@ -51,4 +51,4 @@ } ] ] -} \ No newline at end of file +} diff --git a/src-admin/src/Tabs/Bridges.tsx b/src-admin/src/Tabs/Bridges.tsx index ef24a8f6..5f9125c6 100644 --- a/src-admin/src/Tabs/Bridges.tsx +++ b/src-admin/src/Tabs/Bridges.tsx @@ -1554,7 +1554,7 @@ export class Bridges extends BridgesAndDevices { }} sx={styles.devicesHeader} > - {this.props.alive && bridge.enabled ? ( + {this.props.alive && bridge.enabled && bridge.list.length ? ( ; states: Record; @@ -230,7 +232,10 @@ class Controller extends Component { if (state?.val) { this.setState({ discoveryRunning: true }); } else { - this.setState({ discoveryRunning: false }); + this.setState({ + discoveryRunning: false, + discoveryDone: !!this.state.discovered.length, // Leave dialog open if we found devices + }); } return; } @@ -616,6 +621,8 @@ class Controller extends Component { onClick={async () => { await this.stopDiscovery(); this.setState({ + discoveryDone: false, + discovered: [], showQrCodeDialog: device, }); }} @@ -657,11 +664,13 @@ class Controller extends Component { * Stop discovering devices */ async stopDiscovery(): Promise { + if (!this.state.discoveryRunning) { + // Nothing to stop if no Discovery is running + return; + } console.log('Stop discovery'); await this.props.socket.sendTo(`matter.${this.props.instance}`, 'controllerDiscoveryStop', {}); - - this.setState({ discoveryDone: !!this.state.discovered.length }); } /** @@ -765,8 +774,8 @@ class Controller extends Component { window.alert(`Error on discovery: ${result.error}`); } else if (result.result) { this.setState({ + discoveryDone: !!result.result.length, discovered: result.result, - discoveryDone: true, }); } }); diff --git a/src-admin/src/i18n/de.json b/src-admin/src/i18n/de.json index 592845fb..937f47d7 100644 --- a/src-admin/src/i18n/de.json +++ b/src-admin/src/i18n/de.json @@ -108,6 +108,7 @@ "No devices was added, as they are already in the list or not allowed by license": "Es wurden keine Geräte hinzugefügt, da sie bereits in der Liste sind oder durch die Lizenz nicht zugelassen sind.", "No devices was added, as they are not allowed by license": "Es wurden keine Geräte hinzugefügt, da diese laut Lizenz nicht zulässig sind", "No one device created. Create one, by clicking on the \"+\" button on the left.": "Noch kein Gerät erstellt. Erstellen Sie eins, indem Sie links auf die Schaltfläche „+“ klicken.", + "Node got enabled and will be connected now. It might take a moment until the devices of this node are shown.": "Der Knoten wurde aktiviert und wird nun verbunden. Es kann einen Moment dauern, bis die Geräte dieses Knotens angezeigt werden.", "Not connected": "Nicht verbunden", "Not supported yet": "Noch nicht unterstützt", "Not used devices": "Nicht verwendete Geräte", diff --git a/src-admin/src/i18n/en.json b/src-admin/src/i18n/en.json index 6f314e4b..8788bec7 100644 --- a/src-admin/src/i18n/en.json +++ b/src-admin/src/i18n/en.json @@ -108,6 +108,7 @@ "No devices was added, as they are already in the list or not allowed by license": "No devices was added, as they are already in the list or not allowed by license", "No devices was added, as they are not allowed by license": "No devices was added, as they are not allowed by license", "No one device created. Create one, by clicking on the \"+\" button on the left.": "No one device created. Create one, by clicking on the \"+\" button on the left.", + "Node got enabled and will be connected now. It might take a moment until the devices of this node are shown.": "Node got enabled and will be connected now. It might take a moment until the devices of this node are shown.", "Not connected": "Not connected", "Not supported yet": "Not supported yet", "Not used devices": "Not used devices", diff --git a/src-admin/src/i18n/es.json b/src-admin/src/i18n/es.json index ea3aed96..59ba9b5f 100644 --- a/src-admin/src/i18n/es.json +++ b/src-admin/src/i18n/es.json @@ -108,6 +108,7 @@ "No devices was added, as they are already in the list or not allowed by license": "No se agregó ningún dispositivo porque ya está en la lista o no está permitido por la licencia.", "No devices was added, as they are not allowed by license": "No se agregaron dispositivos, ya que no están permitidos por la licencia.", "No one device created. Create one, by clicking on the \"+\" button on the left.": "No se ha creado ningún dispositivo. Cree uno haciendo clic en el botón \"+\" de la izquierda.", + "Node got enabled and will be connected now. It might take a moment until the devices of this node are shown.": "El nodo se ha activado y se conectará ahora. Puede tardar un momento hasta que se muestren los dispositivos de este nodo.", "Not connected": "No conectado", "Not supported yet": "Aún no es compatible", "Not used devices": "Dispositivos no usados", diff --git a/src-admin/src/i18n/fr.json b/src-admin/src/i18n/fr.json index 03adf5ec..996ffea0 100644 --- a/src-admin/src/i18n/fr.json +++ b/src-admin/src/i18n/fr.json @@ -108,6 +108,7 @@ "No devices was added, as they are already in the list or not allowed by license": "Aucun appareil n'a été ajouté, car ils sont déjà dans la liste ou ne sont pas autorisés par la licence", "No devices was added, as they are not allowed by license": "Aucun appareil n'a été ajouté, car ils ne sont pas autorisés par la licence", "No one device created. Create one, by clicking on the \"+\" button on the left.": "Aucun appareil n'a été créé. Créez-en un en cliquant sur le bouton « + » à gauche.", + "Node got enabled and will be connected now. It might take a moment until the devices of this node are shown.": "Le nœud a été activé et va être connecté maintenant. Il peut s'écouler un certain temps avant que les appareils de ce nœud ne soient affichés.", "Not connected": "Pas connecté", "Not supported yet": "Pas encore pris en charge", "Not used devices": "Appareils non utilisés", diff --git a/src-admin/src/i18n/it.json b/src-admin/src/i18n/it.json index f5c17b23..f7dd0027 100644 --- a/src-admin/src/i18n/it.json +++ b/src-admin/src/i18n/it.json @@ -108,6 +108,7 @@ "No devices was added, as they are already in the list or not allowed by license": "Nessun dispositivo è stato aggiunto, poiché sono già presenti nell'elenco o non sono consentiti dalla licenza", "No devices was added, as they are not allowed by license": "Nessun dispositivo è stato aggiunto, poiché non sono consentiti dalla licenza", "No one device created. Create one, by clicking on the \"+\" button on the left.": "Nessun dispositivo creato. Creane uno, cliccando sul pulsante \"+\" a sinistra.", + "Node got enabled and will be connected now. It might take a moment until the devices of this node are shown.": "Il nodo è stato abilitato e sarà ora connesso. Potrebbe essere necessario un po' di tempo prima che vengano visualizzati i dispositivi di questo nodo.", "Not connected": "Non collegata", "Not supported yet": "Non ancora supportato", "Not used devices": "Dispositivi non utilizzati", diff --git a/src-admin/src/i18n/nl.json b/src-admin/src/i18n/nl.json index 8192b191..6bdcfba3 100644 --- a/src-admin/src/i18n/nl.json +++ b/src-admin/src/i18n/nl.json @@ -108,6 +108,7 @@ "No devices was added, as they are already in the list or not allowed by license": "Er zijn geen apparaten toegevoegd, omdat ze al in de lijst staan of niet zijn toegestaan door de licentie", "No devices was added, as they are not allowed by license": "Er zijn geen apparaten toegevoegd, omdat deze niet zijn toegestaan volgens de licentie", "No one device created. Create one, by clicking on the \"+\" button on the left.": "Geen enkel apparaat gemaakt. Maak er een door op de \"+\" knop aan de linkerkant te klikken.", + "Node got enabled and will be connected now. It might take a moment until the devices of this node are shown.": "Node is ingeschakeld en wordt nu verbonden. Het kan even duren voordat de apparaten van dit knooppunt worden weergegeven.", "Not connected": "Niet verbonden", "Not supported yet": "Nog niet ondersteund", "Not used devices": "Niet gebruikte apparaten", diff --git a/src-admin/src/i18n/pl.json b/src-admin/src/i18n/pl.json index 8cc7fea9..418a9c69 100644 --- a/src-admin/src/i18n/pl.json +++ b/src-admin/src/i18n/pl.json @@ -108,6 +108,7 @@ "No devices was added, as they are already in the list or not allowed by license": "Nie dodano żadnych urządzeń, ponieważ znajdują się już na liście lub nie są dozwolone przez licencję", "No devices was added, as they are not allowed by license": "Nie dodano żadnych urządzeń, ponieważ nie są one dozwolone przez licencję", "No one device created. Create one, by clicking on the \"+\" button on the left.": "Nie utworzono żadnego urządzenia. Utwórz je, klikając przycisk „+” po lewej stronie.", + "Node got enabled and will be connected now. It might take a moment until the devices of this node are shown.": "Węzeł został włączony i zostanie teraz połączony. Wyświetlenie urządzeń tego węzła może chwilę potrwać.", "Not connected": "Nie połączony", "Not supported yet": "Jeszcze nieobsługiwane", "Not used devices": "Nieużywane urządzenia", diff --git a/src-admin/src/i18n/pt.json b/src-admin/src/i18n/pt.json index 761dabc1..9a521d8e 100644 --- a/src-admin/src/i18n/pt.json +++ b/src-admin/src/i18n/pt.json @@ -108,6 +108,7 @@ "No devices was added, as they are already in the list or not allowed by license": "Nenhum dispositivo foi adicionado, pois eles já estão na lista ou não são permitidos pela licença", "No devices was added, as they are not allowed by license": "Nenhum dispositivo foi adicionado, pois não são permitidos pela licença", "No one device created. Create one, by clicking on the \"+\" button on the left.": "Nenhum dispositivo criado. Crie um, clicando no botão \"+\" à esquerda.", + "Node got enabled and will be connected now. It might take a moment until the devices of this node are shown.": "O nó foi ativado e será ligado agora. Pode demorar um pouco até que os dispositivos deste nó sejam mostrados.", "Not connected": "Não conectado", "Not supported yet": "Ainda não suportado", "Not used devices": "Dispositivos não usados", diff --git a/src-admin/src/i18n/ru.json b/src-admin/src/i18n/ru.json index 0d7dd098..93b85157 100644 --- a/src-admin/src/i18n/ru.json +++ b/src-admin/src/i18n/ru.json @@ -108,6 +108,7 @@ "No devices was added, as they are already in the list or not allowed by license": "Устройства не были добавлены, так как они уже есть в списке или не разрешены лицензией.", "No devices was added, as they are not allowed by license": "Устройства не добавлены, так как они не разрешены лицензией.", "No one device created. Create one, by clicking on the \"+\" button on the left.": "Ни одно устройство не создано. Создайте его, нажав на кнопку \"+\" слева.", + "Node got enabled and will be connected now. It might take a moment until the devices of this node are shown.": "Узел включен и теперь будет подключен. Может потребоваться некоторое время, чтобы отобразились устройства этого узла.", "Not connected": "Не подключен", "Not supported yet": "Пока не поддерживается", "Not used devices": "Не используемые устройства", diff --git a/src-admin/src/i18n/uk.json b/src-admin/src/i18n/uk.json index 1bfbff97..b380d7e6 100644 --- a/src-admin/src/i18n/uk.json +++ b/src-admin/src/i18n/uk.json @@ -108,6 +108,7 @@ "No devices was added, as they are already in the list or not allowed by license": "Пристрої не додано, оскільки вони вже є в списку або заборонені ліцензією", "No devices was added, as they are not allowed by license": "Жодних пристроїв не додано, оскільки вони заборонені ліцензією", "No one device created. Create one, by clicking on the \"+\" button on the left.": "Жодного пристрою не створено. Створіть його, натиснувши кнопку «+» ліворуч.", + "Node got enabled and will be connected now. It might take a moment until the devices of this node are shown.": "Вузол увімкнено, і зараз його буде підключено. Може минути деякий час, доки не відобразяться пристрої цього вузла.", "Not connected": "Не з'єднано", "Not supported yet": "Ще не підтримується", "Not used devices": "Пристрої не використовувалися", diff --git a/src-admin/src/i18n/zh-cn.json b/src-admin/src/i18n/zh-cn.json index ff54c9cd..0344b03d 100644 --- a/src-admin/src/i18n/zh-cn.json +++ b/src-admin/src/i18n/zh-cn.json @@ -108,6 +108,7 @@ "No devices was added, as they are already in the list or not allowed by license": "未添加任何设备,因为它们已在列表中或许可证不允许", "No devices was added, as they are not allowed by license": "未添加任何设备,因为许可证不允许这些设备", "No one device created. Create one, by clicking on the \"+\" button on the left.": "尚未创建设备。点击左侧的“+”按钮创建一个。", + "Node got enabled and will be connected now. It might take a moment until the devices of this node are shown.": "Node got enabled and will be connected now. It might take a moment until the devices of this node are shown.", "Not connected": "未连接", "Not supported yet": "尚不支持", "Not used devices": "未使用的设备", diff --git a/src/lib/DeviceManagement.ts b/src/lib/DeviceManagement.ts index 58adf796..11f3348f 100644 --- a/src/lib/DeviceManagement.ts +++ b/src/lib/DeviceManagement.ts @@ -10,6 +10,7 @@ import { type InstanceDetails, type JsonFormSchema, type JsonFormData, + type ConfigConnectionType, DeviceManagement, ACTIONS, } from '@iobroker/dm-utils'; @@ -179,6 +180,7 @@ class MatterAdapterDeviceManagement extends DeviceManagement { // remove null actions actions = actions?.filter(it => it) || []; + const connectionType = ioNode.connectionType; const res = new Array(); const node: DeviceInfo = { id, @@ -187,6 +189,7 @@ class MatterAdapterDeviceManagement extends DeviceManagement { ...details, status, enabled: isEnabled, + connectionType, hasDetails: true, actions: actions.length ? (actions as DeviceAction<'adapter'>[]) : undefined, color: 'secondary', @@ -202,7 +205,7 @@ class MatterAdapterDeviceManagement extends DeviceManagement { if (isEnabled) { let deviceCount = 0; for (const device of ioNode.devices.values()) { - const deviceInfo = await this.#getNodeDeviceEntries(device, id, details, isConnected); + const deviceInfo = await this.#getNodeDeviceEntries(device, id, details, isConnected, connectionType); res.push(deviceInfo); deviceCount++; } @@ -223,17 +226,18 @@ class MatterAdapterDeviceManagement extends DeviceManagement { nodeId: string, nodeDetails: NodeDetails, nodeConnected: boolean, + nodeConnectionType: ConfigConnectionType, ): Promise { + const icon = device.iconDeviceType; const data: DeviceInfo = { id: `${nodeId}-${device.number}`, name: device.name, - icon: device.ioBrokerDevice.deviceType, + icon, ...nodeDetails, status: await device.getStatus({ connection: nodeConnected ? 'connected' : 'disconnected', }), - // TODO: provide here the valid connection type (thread, wifi or bluetooth) - connectionType: 'wifi', + connectionType: nodeConnectionType, hasDetails: true, actions: [ { @@ -250,9 +254,9 @@ class MatterAdapterDeviceManagement extends DeviceManagement { }, ], group: { - key: `device/${device.ioBrokerDevice.deviceType}`, - name: this.#adapter.getText(device.ioBrokerDevice.deviceType as string), - icon: device.ioBrokerDevice.deviceType, + key: `device/${device.deviceType}`, + name: this.#adapter.getText(device.deviceType), + icon, }, }; @@ -286,7 +290,7 @@ class MatterAdapterDeviceManagement extends DeviceManagement { if (node.isEnabled) { await context.showMessage( this.#adapter.t( - 'Node enabled and will be connected now. It might take a moment until the devices of this node are shown.', + 'Node got enabled and will be connected now. It might take a moment until the devices of this node are shown.', ), ); } @@ -295,32 +299,19 @@ class MatterAdapterDeviceManagement extends DeviceManagement { } async #handleOnStatusNode(node: GeneralMatterNode, context: ActionContext): Promise<{ refresh: DeviceRefresh }> { - // This is only an example how to react on clicking on Status - await context.showForm( - { - type: 'panel', - items: { - connected: { - type: 'checkbox', - label: this.#adapter.getText('Connected'), - sm: 12, - readOnly: true, - default: node.node.isConnected, - }, + const schema = convertDataToJsonConfig(await node.getConnectionStatus()); + + await context.showForm(schema, { + data: {}, + maxWidth: 'md', + title: this.#adapter.getText('Connection Status'), + buttons: [ + { + type: 'cancel', + label: this.#adapter.getText('Close'), }, - }, - { - data: {}, - maxWidth: 'md', - title: this.#adapter.getText('Status of Node'), - buttons: [ - { - type: 'cancel', - label: this.#adapter.getText('Close'), - }, - ], - }, - ); + ], + }); return { refresh: false }; } @@ -707,7 +698,7 @@ class MatterAdapterDeviceManagement extends DeviceManagement { return { error: 'Device not found' }; } - const schema = convertDataToJsonConfig(await device.getDeviceDetails()); + const schema = convertDataToJsonConfig(await device.getDeviceDetails(node.isConnected)); return { id, schema, data: {} }; } diff --git a/src/lib/devices/AirCondition.ts b/src/lib/devices/AirCondition.ts index 9b601ae7..35f66d35 100644 --- a/src/lib/devices/AirCondition.ts +++ b/src/lib/devices/AirCondition.ts @@ -111,7 +111,7 @@ class AirCondition extends ElectricityDataDevice { return this.#levelState.value; } - async setLevel(value: number): Promise { + setLevel(value: number): Promise { if (!this.#levelState) { throw new Error('Level state not found'); } @@ -132,7 +132,7 @@ class AirCondition extends ElectricityDataDevice { return this.#powerState.value; } - async setPower(value: boolean): Promise { + setPower(value: boolean): Promise { if (!this.#powerState) { throw new Error('Power state not found'); } @@ -153,7 +153,7 @@ class AirCondition extends ElectricityDataDevice { return this.#boostState.value; } - async setBoost(value: boolean | number): Promise { + setBoost(value: boolean | number): Promise { if (!this.#boostState) { throw new Error('Boost state not found'); } @@ -167,7 +167,7 @@ class AirCondition extends ElectricityDataDevice { return this.#speedState.value; } - async setSpeed(value: AirConditionerSpeed): Promise { + setSpeed(value: AirConditionerSpeed): Promise { if (!this.#speedState) { throw new Error('Speed state not found'); } @@ -188,7 +188,7 @@ class AirCondition extends ElectricityDataDevice { return this.#SwingState.value; } - async setSwing(value: AirConditionerSwing): Promise { + setSwing(value: AirConditionerSwing): Promise { if (!this.#SwingState) { throw new Error('Swing state not found'); } @@ -209,11 +209,11 @@ class AirCondition extends ElectricityDataDevice { return this.#modeState.value; } - async setMode(mode: AirConditionerMode): Promise { + setMode(mode: AirConditionerMode): Promise { if (!this.#modeState) { throw new Error('Mode state not found'); } - await this.#modeState.setValue(mode); + return this.#modeState.setValue(mode); } getModes(): AirConditionerMode[] { diff --git a/src/lib/devices/Blind.ts b/src/lib/devices/Blind.ts index 7a54674c..a920e4cb 100644 --- a/src/lib/devices/Blind.ts +++ b/src/lib/devices/Blind.ts @@ -11,19 +11,18 @@ class Blind extends BlindButtons { this._construction.push( this.addDeviceStates([ - // actual value first, as it will be read first { name: 'ACTUAL', valueType: ValueType.NumberPercent, accessType: StateAccessType.Read, - type: PropertyType.Level, + type: PropertyType.LevelActual, callback: state => (this.#getLevelState = state), }, { name: 'SET', valueType: ValueType.NumberPercent, accessType: StateAccessType.ReadWrite, - type: PropertyType.LevelActual, + type: PropertyType.Level, callback: state => (this.#setLevelState = state), }, ]), @@ -37,7 +36,7 @@ class Blind extends BlindButtons { return (this.#getLevelState || this.#setLevelState)?.value; } - async setLevel(value: number): Promise { + setLevel(value: number): Promise { if (!this.#setLevelState) { throw new Error('Level state not found'); } @@ -59,13 +58,6 @@ class Blind extends BlindButtons { return this.#getLevelState.value; } - async setLevelActual(value: number): Promise { - if (!this.#setLevelState) { - throw new Error('Level state not found'); - } - await this.#setLevelState.setValue(value); - } - async updateLevelActual(value: number): Promise { if (!this.#getLevelState) { throw new Error('Level state not found'); diff --git a/src/lib/devices/BlindButtons.ts b/src/lib/devices/BlindButtons.ts index 495ed15d..dd07d1a3 100644 --- a/src/lib/devices/BlindButtons.ts +++ b/src/lib/devices/BlindButtons.ts @@ -96,21 +96,21 @@ class BlindButtons extends GenericDevice { ); } - async setStop(): Promise { + setStop(): Promise { if (!this.#setStopState) { throw new Error('Stop state not found'); } return this.#setStopState.setValue(true); } - async setOpen(): Promise { + setOpen(): Promise { if (!this.#setOpenState) { throw new Error('Open state not found'); } return this.#setOpenState.setValue(true); } - async setClose(): Promise { + setClose(): Promise { if (!this.#setCloseState) { throw new Error('Close state not found'); } @@ -124,7 +124,7 @@ class BlindButtons extends GenericDevice { return (this.#getTiltState || this.#setTiltState)?.value; } - async setTiltLevel(value: number): Promise { + setTiltLevel(value: number): Promise { if (!this.#setTiltState) { throw new Error('Tilt state not found'); } @@ -147,28 +147,28 @@ class BlindButtons extends GenericDevice { } async updateTiltLevelActual(value: number): Promise { - if (!this.#setTiltState) { + if (!this.#getTiltState) { throw new Error('Level state not found'); } - await this.#setTiltState.updateValue(value); + await this.#getTiltState.updateValue(value); await this.#setTiltState?.updateValue(value); } - async setTiltStop(): Promise { + setTiltStop(): Promise { if (!this.#setTiltStopState) { throw new Error('Tilt stop state not found'); } return this.#setTiltStopState.setValue(true); } - async setTiltOpen(): Promise { + setTiltOpen(): Promise { if (!this.#setTiltOpenState) { throw new Error('Tilt open state not found'); } return this.#setTiltOpenState.setValue(true); } - async setTiltClose(): Promise { + setTiltClose(): Promise { if (!this.#setTiltCloseState) { throw new Error('Tilt close state not found'); } diff --git a/src/lib/devices/Button.ts b/src/lib/devices/Button.ts index e02e081c..7e3f21c7 100644 --- a/src/lib/devices/Button.ts +++ b/src/lib/devices/Button.ts @@ -20,7 +20,7 @@ class Button extends GenericDevice { ); } - async setPress(): Promise { + setPress(): Promise { if (!this.#setPressState) { throw new Error('Press state not found'); } diff --git a/src/lib/devices/ButtonSensor.ts b/src/lib/devices/ButtonSensor.ts index db39d124..d089f959 100644 --- a/src/lib/devices/ButtonSensor.ts +++ b/src/lib/devices/ButtonSensor.ts @@ -35,11 +35,11 @@ class ButtonSensor extends GenericDevice { return this.#setPressState.value; } - async updatePress(): Promise { + updatePress(): Promise { if (!this.#setPressState) { throw new Error('Press state not found'); } - await this.#setPressState.updateValue(true); + return this.#setPressState.updateValue(true); } hasPressLong(): boolean { @@ -53,11 +53,11 @@ class ButtonSensor extends GenericDevice { return this.#setPressLongState.value; } - async updatePressLong(): Promise { + updatePressLong(): Promise { if (!this.#setPressLongState) { throw new Error('PressLong state not found'); } - await this.#setPressLongState.updateValue(true); + return this.#setPressLongState.updateValue(true); } } diff --git a/src/lib/devices/Camera.ts b/src/lib/devices/Camera.ts index f65f93d3..a27ccc9c 100644 --- a/src/lib/devices/Camera.ts +++ b/src/lib/devices/Camera.ts @@ -83,7 +83,7 @@ class Camera extends GenericDevice { return this.#autoFocusState.value; } - async setAutoFocus(value: boolean): Promise { + setAutoFocus(value: boolean): Promise { if (!this.#autoFocusState) { throw new Error('AutoFocus state not found'); } @@ -97,7 +97,7 @@ class Camera extends GenericDevice { return this.#autoWhiteBalanceState.value; } - async setAutoWhiteBalance(value: boolean): Promise { + setAutoWhiteBalance(value: boolean): Promise { if (!this.#autoWhiteBalanceState) { throw new Error('AutoWhiteBalance state not found'); } @@ -111,7 +111,7 @@ class Camera extends GenericDevice { return this.#brightnessState.value; } - async setBrightness(value: boolean): Promise { + setBrightness(value: boolean): Promise { if (!this.#brightnessState) { throw new Error('Brightness state not found'); } @@ -125,7 +125,7 @@ class Camera extends GenericDevice { return this.#nightModeState.value; } - async setNightMode(value: boolean): Promise { + setNightMode(value: boolean): Promise { if (!this.#nightModeState) { throw new Error('NightMode state not found'); } @@ -139,7 +139,7 @@ class Camera extends GenericDevice { return this.#ptzState.value; } - async setPtz(value: number): Promise { + setPtz(value: number): Promise { if (!this.#ptzState) { throw new Error('PTZ state not found'); } diff --git a/src/lib/devices/Cie.ts b/src/lib/devices/Cie.ts index d92993d1..ff46176f 100644 --- a/src/lib/devices/Cie.ts +++ b/src/lib/devices/Cie.ts @@ -45,7 +45,7 @@ class Cie extends Ct { return undefined; } - async setCie(value: string): Promise { + setCie(value: string): Promise { if (!this.#cie) { throw new Error('CIE state not found'); } @@ -73,28 +73,28 @@ class Cie extends Ct { return this.#cie.setValue(`[${this.getXy()?.x},${y}]`); } - async updateCie(value: string): Promise { + updateCie(value: string): Promise { if (!this.#cie) { throw new Error('CIE state not found'); } return this.#cie.updateValue(value); } - async updateXy(x: number, y: number): Promise { + updateXy(x: number, y: number): Promise { if (!this.#cie) { throw new Error('CIE state not found'); } return this.#cie.updateValue(`[${x},${y}]`); } - async updateX(x: number): Promise { + updateX(x: number): Promise { if (!this.#cie) { throw new Error('CIE state not found'); } return this.#cie.updateValue(`[${x},${this.getXy()?.y}]`); } - async updateY(y: number): Promise { + updateY(y: number): Promise { if (!this.#cie) { throw new Error('CIE state not found'); } diff --git a/src/lib/devices/Ct.ts b/src/lib/devices/Ct.ts index 1f021588..167c3e21 100644 --- a/src/lib/devices/Ct.ts +++ b/src/lib/devices/Ct.ts @@ -80,17 +80,17 @@ class Ct extends ElectricityDataDevice { return this.#dimmerState.value; } - async updateDimmer(value: number): Promise { + updateDimmer(value: number): Promise { if (!this.#dimmerState) { if (!this.#brightnessState) { throw new Error('Dimmer state not found'); } return this.#brightnessState.updateValue(value); } - await this.#dimmerState.updateValue(value); + return this.#dimmerState.updateValue(value); } - async setDimmer(value: number): Promise { + setDimmer(value: number): Promise { if (!this.#dimmerState) { if (!this.#brightnessState) { throw new Error('Dimmer state not found'); @@ -111,14 +111,14 @@ class Ct extends ElectricityDataDevice { return this.#brightnessState.value; } - async updateBrightness(value: number): Promise { + updateBrightness(value: number): Promise { if (!this.#brightnessState) { throw new Error('Brightness state not found'); } - await this.#brightnessState.updateValue(value); + return this.#brightnessState.updateValue(value); } - async setBrightness(value: number): Promise { + setBrightness(value: number): Promise { if (!this.#brightnessState) { throw new Error('Brightness state not found'); } @@ -139,14 +139,14 @@ class Ct extends ElectricityDataDevice { return this.#temperatureState.getMinMax(); } - async updateTemperature(value: number): Promise { + updateTemperature(value: number): Promise { if (!this.#temperatureState) { throw new Error('Temperature state not found'); } - await this.#temperatureState.updateValue(value); + return this.#temperatureState.updateValue(value); } - async setTemperature(value: number): Promise { + setTemperature(value: number): Promise { if (!this.#temperatureState) { throw new Error('Temperature state not found'); } @@ -160,7 +160,7 @@ class Ct extends ElectricityDataDevice { return (this.#getPowerState || this.#setPowerState)?.value; } - async setPower(value: boolean): Promise { + setPower(value: boolean): Promise { if (!this.#setPowerState) { throw new Error('On state not found'); } diff --git a/src/lib/devices/Dimmer.ts b/src/lib/devices/Dimmer.ts index 76c6acc5..9ae3d5f7 100644 --- a/src/lib/devices/Dimmer.ts +++ b/src/lib/devices/Dimmer.ts @@ -71,7 +71,7 @@ class Dimmer extends ElectricityDataDevice { return (this.#getLevelState || this.#setLevelState)?.value; } - async setLevel(value: number): Promise { + setLevel(value: number): Promise { if (!this.#setLevelState) { throw new Error('Level state not found'); } @@ -115,10 +115,7 @@ class Dimmer extends ElectricityDataDevice { return undefined; } - async setPower(value: boolean): Promise { - if (!this.#setPowerState && !this.#setLevelState) { - throw new Error('Power state not found'); - } + setPower(value: boolean): Promise { if (this.#setPowerState) { return this.#setPowerState.setValue(value); } @@ -134,6 +131,7 @@ class Dimmer extends ElectricityDataDevice { } return this.#setLevelState.setValue(0); } + throw new Error('Power state not found'); } async updatePower(value: boolean): Promise { diff --git a/src/lib/devices/Gate.ts b/src/lib/devices/Gate.ts index 545b77bb..44717967 100644 --- a/src/lib/devices/Gate.ts +++ b/src/lib/devices/Gate.ts @@ -44,14 +44,14 @@ class Gate extends GenericDevice { return (this.#getLevelState || this.#setLevelState)?.value; } - async setLevel(value: number): Promise { + setLevel(value: number): Promise { if (!this.#setLevelState) { throw new Error('Level state not found'); } return this.#setLevelState.setValue(value); } - async setStop(): Promise { + setStop(): Promise { if (!this.#setStopState) { throw new Error('Stop state not found'); } diff --git a/src/lib/devices/Hue.ts b/src/lib/devices/Hue.ts index 6a1401b4..49690671 100644 --- a/src/lib/devices/Hue.ts +++ b/src/lib/devices/Hue.ts @@ -36,14 +36,14 @@ class Hue extends Ct { return this.#hue.value; } - async setHue(value: number): Promise { + setHue(value: number): Promise { if (!this.#hue) { throw new Error('HUE state not found'); } return this.#hue.setValue(value); } - async updateHue(value: number): Promise { + updateHue(value: number): Promise { if (!this.#hue) { throw new Error('HUE state not found'); } @@ -57,14 +57,14 @@ class Hue extends Ct { return this.#saturationState.value; } - async setSaturation(value: number): Promise { + setSaturation(value: number): Promise { if (!this.#saturationState) { throw new Error('Saturation state not found'); } return this.#saturationState.setValue(value); } - async updateSaturation(value: number): Promise { + updateSaturation(value: number): Promise { if (!this.#saturationState) { throw new Error('Saturation state not found'); } diff --git a/src/lib/devices/Humidity.ts b/src/lib/devices/Humidity.ts index 791cbe8b..25a71ee2 100644 --- a/src/lib/devices/Humidity.ts +++ b/src/lib/devices/Humidity.ts @@ -27,11 +27,11 @@ class Humidity extends GenericDevice { return this.#getHumidityState.value; } - async updateHumidity(value: number): Promise { + updateHumidity(value: number): Promise { if (!this.#getHumidityState) { throw new Error('Value state not found'); } - await this.#getHumidityState.updateValue(value); + return this.#getHumidityState.updateValue(value); } } diff --git a/src/lib/devices/Light.ts b/src/lib/devices/Light.ts index 1f88a6b4..72745103 100644 --- a/src/lib/devices/Light.ts +++ b/src/lib/devices/Light.ts @@ -45,11 +45,11 @@ class Light extends ElectricityDataDevice { await this.#setPowerState?.updateValue(value); } - async setPower(value: boolean): Promise { + setPower(value: boolean): Promise { if (!this.#setPowerState) { throw new Error('Set state not found'); } - await this.#setPowerState.setValue(value); + return this.#setPowerState.setValue(value); } getPowerActual(): boolean | undefined { diff --git a/src/lib/devices/Lock.ts b/src/lib/devices/Lock.ts index f685bdbf..7afe90c5 100644 --- a/src/lib/devices/Lock.ts +++ b/src/lib/devices/Lock.ts @@ -44,7 +44,7 @@ class Lock extends GenericDevice { return (this.#getLockState || this.#setLockState)?.value; } - async setLockState(value: boolean): Promise { + setLockState(value: boolean): Promise { if (!this.#setLockState) { throw new Error('Level state not found'); } @@ -55,12 +55,8 @@ class Lock extends GenericDevice { if (!this.#getLockState && !this.#setLockState) { throw new Error('Level state not found'); } - if (this.#getLockState) { - await this.#getLockState.updateValue(value); - } - if (this.#setLockState) { - await this.#setLockState.updateValue(value); - } + await this.#getLockState?.updateValue(value); + await this.#setLockState?.updateValue(value); } getLockStateActual(): boolean | undefined { @@ -78,7 +74,7 @@ class Lock extends GenericDevice { await this.#setLockState?.updateValue(value); } - async setOpen(): Promise { + setOpen(): Promise { if (!this.#setOpenState) { throw new Error('Open state not found'); } diff --git a/src/lib/devices/Media.ts b/src/lib/devices/Media.ts index 55d925ee..9ecaad2a 100644 --- a/src/lib/devices/Media.ts +++ b/src/lib/devices/Media.ts @@ -199,35 +199,35 @@ class Media extends GenericDevice { return this.#getStateState.value; } - async setPlay(): Promise { + setPlay(): Promise { if (!this.#setPlayState) { throw new Error('Play state not found'); } return this.#setPlayState.setValue(true); } - async setPause(): Promise { + setPause(): Promise { if (!this.#setPauseState) { throw new Error('Pause state not found'); } return this.#setPauseState.setValue(true); } - async setStop(): Promise { + setStop(): Promise { if (!this.#setStopState) { throw new Error('Stop state not found'); } return this.#setStopState.setValue(true); } - async setNext(): Promise { + setNext(): Promise { if (!this.#setNextState) { throw new Error('Next state not found'); } return this.#setNextState.setValue(true); } - async setPrevious(): Promise { + setPrevious(): Promise { if (!this.#setPrevState) { throw new Error('Prev state not found'); } @@ -241,7 +241,7 @@ class Media extends GenericDevice { return this.#shuffleState.value; } - async setShuffle(value: boolean): Promise { + setShuffle(value: boolean): Promise { if (!this.#shuffleState) { throw new Error('Shuffle state not found'); } @@ -255,7 +255,7 @@ class Media extends GenericDevice { return this.#repeatState.value; } - async setRepeat(value: number): Promise { + setRepeat(value: number): Promise { if (!this.#repeatState) { throw new Error('Repeat state not found'); } @@ -304,7 +304,7 @@ class Media extends GenericDevice { return this.#getElapsedState.value; } - async setSeek(value: number): Promise { + setSeek(value: number): Promise { if (!this.#setSeekState) { throw new Error('Seek state not found'); } @@ -339,7 +339,7 @@ class Media extends GenericDevice { return this.#getVolumeState.value; } - async setVolume(value: number): Promise { + setVolume(value: number): Promise { if (!this.#setVolumeState) { throw new Error('Volume state not found'); } @@ -353,7 +353,7 @@ class Media extends GenericDevice { return this.#muteState.value; } - async setMute(value: boolean): Promise { + setMute(value: boolean): Promise { if (!this.#muteState) { throw new Error('Mute state not found'); } diff --git a/src/lib/devices/Rgb.ts b/src/lib/devices/Rgb.ts index 2d0b5b61..17c1da17 100644 --- a/src/lib/devices/Rgb.ts +++ b/src/lib/devices/Rgb.ts @@ -52,11 +52,11 @@ class Rgb extends Ct { return this.#red.value; } - async setRed(value: number): Promise { + setRed(value: number): Promise { if (!this.#red) { throw new Error('Red state not found'); } - await this.#red.setValue(value); + return this.#red.setValue(value); } getGreen(): number | undefined { @@ -66,11 +66,11 @@ class Rgb extends Ct { return this.#green.value; } - async setGreen(value: number): Promise { + setGreen(value: number): Promise { if (!this.#green) { throw new Error('Red state not found'); } - await this.#green.setValue(value); + return this.#green.setValue(value); } getBlue(): number | undefined { @@ -80,11 +80,11 @@ class Rgb extends Ct { return this.#blue.value; } - async setBlue(value: number): Promise { + setBlue(value: number): Promise { if (!this.#blue) { throw new Error('Red state not found'); } - await this.#blue.setValue(value); + return this.#blue.setValue(value); } getWhite(): number | undefined { @@ -94,11 +94,11 @@ class Rgb extends Ct { return this.#white.value; } - async setWhite(value: number): Promise { + setWhite(value: number): Promise { if (!this.#white) { throw new Error('Red state not found'); } - await this.#white.setValue(value); + return this.#white.setValue(value); } } diff --git a/src/lib/devices/RgbSingle.ts b/src/lib/devices/RgbSingle.ts index 30e41a65..cf5fb425 100644 --- a/src/lib/devices/RgbSingle.ts +++ b/src/lib/devices/RgbSingle.ts @@ -28,7 +28,7 @@ class RgbSingle extends Ct { return this.#rgb.value; } - async setRgb(value: string): Promise { + setRgb(value: string): Promise { if (!this.#rgb) { throw new Error('RGB state not found'); } diff --git a/src/lib/devices/RgbwSingle.ts b/src/lib/devices/RgbwSingle.ts index e86d517c..482d9219 100644 --- a/src/lib/devices/RgbwSingle.ts +++ b/src/lib/devices/RgbwSingle.ts @@ -28,7 +28,7 @@ class RgbwSingle extends Ct { return this.#rgbw.value; } - async setRgbw(value: string): Promise { + setRgbw(value: string): Promise { if (!this.#rgbw) { throw new Error('RGBW state not found'); } diff --git a/src/lib/devices/Slider.ts b/src/lib/devices/Slider.ts index 3afe81cd..120672c6 100644 --- a/src/lib/devices/Slider.ts +++ b/src/lib/devices/Slider.ts @@ -36,7 +36,7 @@ class Slider extends GenericDevice { return (this.#getLevelState || this.#setLevelState)?.value; } - async setLevel(value: number): Promise { + setLevel(value: number): Promise { if (!this.#setLevelState) { throw new Error('Level state not found'); } diff --git a/src/lib/devices/Socket.ts b/src/lib/devices/Socket.ts index 92978352..48f07128 100644 --- a/src/lib/devices/Socket.ts +++ b/src/lib/devices/Socket.ts @@ -37,11 +37,11 @@ class Socket extends ElectricityDataDevice { return (this.#getPowerState || this.#setPowerState)?.value; } - async setPower(value: boolean): Promise { + setPower(value: boolean): Promise { if (!this.#setPowerState) { throw new Error('Level state not found'); } - await this.#setPowerState.setValue(value); + return this.#setPowerState.setValue(value); } async updatePower(value: boolean): Promise { diff --git a/src/lib/devices/Temperature.ts b/src/lib/devices/Temperature.ts index 0e2b764c..a327e2ce 100644 --- a/src/lib/devices/Temperature.ts +++ b/src/lib/devices/Temperature.ts @@ -38,11 +38,11 @@ class Temperature extends GenericDevice { return this.#getTemperatureState.value; } - async updateTemperature(value: number): Promise { + updateTemperature(value: number): Promise { if (!this.#getTemperatureState) { throw new Error('Value state not found'); } - await this.#getTemperatureState.updateValue(value); + return this.#getTemperatureState.updateValue(value); } hasHumidity(): boolean { diff --git a/src/lib/devices/Thermostat.ts b/src/lib/devices/Thermostat.ts index 96aaa093..baf2578f 100644 --- a/src/lib/devices/Thermostat.ts +++ b/src/lib/devices/Thermostat.ts @@ -100,14 +100,14 @@ class Thermostat extends GenericDevice { return this.#modeState.getModes(); } - async updateModes(modes: { [key: string]: ThermostatMode }): Promise { + updateModes(modes: { [key: string]: ThermostatMode }): Promise { if (!this.#modeState) { throw new Error('Mode state not found'); } - await this.#modeState.updateModes(modes); + return this.#modeState.updateModes(modes); } - async setMode(mode: ThermostatMode): Promise { + setMode(mode: ThermostatMode): Promise { if (!this.#modeState) { throw new Error('Mode state not found'); } @@ -121,11 +121,11 @@ class Thermostat extends GenericDevice { return this.#modeState.value; } - async updateSetpointMinMax(min: number | undefined, max: number | undefined): Promise { + updateSetpointMinMax(min: number | undefined, max: number | undefined): Promise { if (!this.#levelState) { throw new Error('Level state not found'); } - await this.#levelState.updateMinMax({ min, max }); + return this.#levelState.updateMinMax({ min, max }); } getLevel(): number | undefined { @@ -135,14 +135,14 @@ class Thermostat extends GenericDevice { return this.#levelState.value; } - async setLevel(value: number): Promise { + setLevel(value: number): Promise { if (!this.#levelState) { throw new Error('Level state not found'); } return this.#levelState.setValue(value); } - async updateLevel(value: number): Promise { + updateLevel(value: number): Promise { if (!this.#levelState) { throw new Error('Level state not found'); } @@ -156,6 +156,13 @@ class Thermostat extends GenericDevice { return this.#getTemperatureState.value; } + updateTemperature(value: number): Promise { + if (!this.#getTemperatureState) { + throw new Error('Temperature state not found'); + } + return this.#getTemperatureState.updateValue(value); + } + getPower(): boolean | number | undefined { if (!this.#powerState) { throw new Error('Power state not found'); @@ -163,14 +170,14 @@ class Thermostat extends GenericDevice { return this.#powerState.value; } - async setPower(value: boolean): Promise { + setPower(value: boolean): Promise { if (!this.#powerState) { throw new Error('Power state not found'); } return this.#powerState.setValue(value); } - async updatePower(value: boolean | number): Promise { + updatePower(value: boolean | number): Promise { if (!this.#powerState) { throw new Error('Power state not found'); } @@ -184,6 +191,13 @@ class Thermostat extends GenericDevice { return this.#getHumidityState.value; } + updateHumidity(value: number): Promise { + if (!this.#getHumidityState) { + throw new Error('Humidity state not found'); + } + return this.#getHumidityState.updateValue(value); + } + getBoost(): number | undefined { if (!this.#boostState) { throw new Error('Boost state not found'); @@ -191,7 +205,7 @@ class Thermostat extends GenericDevice { return this.#boostState.value; } - async setBoost(value: number): Promise { + setBoost(value: number): Promise { if (!this.#boostState) { throw new Error('Boost state not found'); } @@ -205,7 +219,7 @@ class Thermostat extends GenericDevice { return this.#partyState.value; } - async setParty(value: boolean | number): Promise { + setParty(value: boolean | number): Promise { if (!this.#partyState) { throw new Error('Party state not found'); } diff --git a/src/lib/devices/VacuumCleaner.ts b/src/lib/devices/VacuumCleaner.ts index 8b841109..f75da23c 100644 --- a/src/lib/devices/VacuumCleaner.ts +++ b/src/lib/devices/VacuumCleaner.ts @@ -161,7 +161,7 @@ class VacuumCleaner extends GenericDevice { return this.#powerState.value; } - async setPower(value: boolean): Promise { + setPower(value: boolean): Promise { if (!this.#powerState) { throw new Error('Power state not found'); } @@ -175,7 +175,7 @@ class VacuumCleaner extends GenericDevice { return this.#modeState.value; } - async setMode(mode: VacuumCleanerMode): Promise { + setMode(mode: VacuumCleanerMode): Promise { if (!this.#modeState) { throw new Error('Mode state not found'); } @@ -210,7 +210,7 @@ class VacuumCleaner extends GenericDevice { return this.#workModeState.value; } - async setWorkMode(mode: VacuumCleanerWorkMode): Promise { + setWorkMode(mode: VacuumCleanerWorkMode): Promise { if (!this.#workModeState) { throw new Error('WorkMode state not found'); } @@ -252,7 +252,7 @@ class VacuumCleaner extends GenericDevice { return this.#getStateState.getModes(); } - async setPause(value: boolean): Promise { + setPause(value: boolean): Promise { if (!this.#pauseState) { throw new Error('Pause state not found'); } diff --git a/src/lib/devices/Volume.ts b/src/lib/devices/Volume.ts index 88e3a63d..ebd4de38 100644 --- a/src/lib/devices/Volume.ts +++ b/src/lib/devices/Volume.ts @@ -44,7 +44,7 @@ class Volume extends GenericDevice { return (this.#getLevelState || this.#setLevelState)?.value; } - async setLevel(value: number): Promise { + setLevel(value: number): Promise { if (!this.#setLevelState) { throw new Error('Level state not found'); } @@ -81,7 +81,7 @@ class Volume extends GenericDevice { return this.#muteState.value; } - async setMute(value: boolean): Promise { + setMute(value: boolean): Promise { if (!this.#muteState) { throw new Error('Mute state not found'); } diff --git a/src/main.ts b/src/main.ts index ed550daf..a408790a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -303,7 +303,12 @@ export class MatterAdapter extends utils.Adapter { } } if (obj.callback) { - this.sendTo(obj.from, obj.command, { error: 'Device or Bridge not found' }, obj.callback); + this.sendTo( + obj.from, + obj.command, + { error: `Device or Bridge ${obj.message.uuid} not found` }, + obj.callback, + ); } return; } diff --git a/src/matter/ControllerNode.ts b/src/matter/ControllerNode.ts index a83e0b12..bf3bf869 100644 --- a/src/matter/ControllerNode.ts +++ b/src/matter/ControllerNode.ts @@ -506,14 +506,13 @@ class Controller implements GeneralNode { ) .then(result => { this.#adapter.log.info(`Discovering stopped. Found ${result.length} devices.`); - if (this.#discovering) { - this.#adapter - .setState('controller.info.discovering', false, true) - .catch(error => this.#adapter.log.info(`Error setting state: ${error}`)); - this.#discovering = false; - if (obj.callback) { - this.#adapter.sendTo(obj.from, obj.command, { result }, obj.callback); - } + this.#adapter + .setState('controller.info.discovering', false, true) + .catch(error => this.#adapter.log.info(`Error setting state: ${error}`)); + this.#discovering = false; + if (obj.callback) { + this.#adapter.log.info(`Sending result to "${JSON.stringify(result)}"`); + this.#adapter.sendTo(obj.from, obj.command, { result }, obj.callback); } }) .catch(error => { diff --git a/src/matter/GeneralMatterNode.ts b/src/matter/GeneralMatterNode.ts index 33a218c6..1938bce3 100644 --- a/src/matter/GeneralMatterNode.ts +++ b/src/matter/GeneralMatterNode.ts @@ -4,6 +4,7 @@ import { BridgedDeviceBasicInformation, GeneralDiagnosticsCluster, WiFiNetworkDiagnosticsCluster, + OperationalCredentialsCluster, } from '@matter/main/clusters'; import { AttributeModel, ClusterModel, CommandModel, MatterModel } from '@matter/main/model'; import { @@ -29,7 +30,8 @@ import type { MatterAdapter } from '../main'; import type { GenericDeviceToIoBroker } from './to-iobroker/GenericDeviceToIoBroker'; import ioBrokerDeviceFabric, { identifyDeviceTypes } from './to-iobroker/ioBrokerFactory'; import type { StructuredJsonFormData } from '../lib/JsonConfigUtils'; -import type { DeviceStatus } from '@iobroker/dm-utils'; +import type { DeviceStatus, ConfigConnectionType } from '@iobroker/dm-utils'; +import { VendorIds } from '../lib/vendorIDs'; export type PairedNodeConfig = { nodeId: NodeId; @@ -1145,4 +1147,56 @@ export class GeneralMatterNode { return status; } + + get connectionType(): ConfigConnectionType { + if (this.node.deviceInformation?.threadConnected) { + return 'thread'; + } else if (this.node.deviceInformation?.wifiConnected) { + return 'wifi'; + } + return 'lan'; + } + + async getConnectionStatus(): Promise { + const result: StructuredJsonFormData = {}; + + result.connection = { + __text__connected: this.node.isConnected + ? 'The node is successfully connected.' + : this.#enabled + ? 'The node is currently not connected.' + : 'The node is disabled.', + status: decamelize(NodeStates[this.node.state]), + }; + + result.connection.address = this.#connectedAddress; + if (this.node.deviceInformation?.threadConnected) { + result.connection.connectedVia = 'Thread'; + } else if (this.node.deviceInformation?.wifiConnected) { + result.connection.connectedVia = 'WiFi'; + } else if (this.node.deviceInformation?.ethernetConnected) { + result.connection.connectedVia = 'Ethernet'; + } + + if (this.node.isConnected) { + const operationalCredentials = this.node.getRootClusterClient(OperationalCredentialsCluster); + if (operationalCredentials) { + result.connection.__header__operationalCredentials = 'Connected Fabrics'; + const ownFabricIndex = await operationalCredentials.getCurrentFabricIndexAttribute(); + const fabrics = await operationalCredentials.getFabricsAttribute(true, false); + fabrics.forEach(fabric => { + const fabricId = fabric.fabricId; + const vendorId = fabric.vendorId; + const vendorName = VendorIds[vendorId] + ? `${VendorIds[vendorId]} (0x${vendorId.toString(16)})` + : `0x${vendorId.toString(16)}`; + result.connection[`fabric${fabricId}__${vendorName}`] = + `${fabric.label}${ownFabricIndex === fabric.fabricIndex ? ' (Own)' : ''}`; + // TODO Add name lookup and button to manage beside own Fabric index + }); + } + } + + return result; + } } diff --git a/src/matter/behaviors/IdentifyServer.ts b/src/matter/behaviors/IdentifyServer.ts new file mode 100644 index 00000000..a1103d6f --- /dev/null +++ b/src/matter/behaviors/IdentifyServer.ts @@ -0,0 +1,9 @@ +import { IdentifyServer } from '@matter/main/behaviors'; + +export class LoggingIdentifyServer extends IdentifyServer {} + +export class LightingIdentifyServer extends IdentifyServer { + override triggerEffect(): void { + // TODO + } +} diff --git a/src/matter/behaviors/IoBrokerContext.ts b/src/matter/behaviors/IoBrokerContext.ts new file mode 100644 index 00000000..409e16b5 --- /dev/null +++ b/src/matter/behaviors/IoBrokerContext.ts @@ -0,0 +1,25 @@ +import { Behavior } from '@matter/main'; +import type GenericDevice from '../../lib/devices/GenericDevice'; +import type { MatterAdapter } from '../../main'; + +export class IoBrokerContextBehavior extends Behavior { + static override readonly id = 'ioBrokerContext'; + declare state: IoBrokerContextBehavior.State; + + override initialize(): void { + if (!this.state.adapter) { + throw new Error('Adapter not set'); + } + if (!this.state.device) { + throw new Error('Device not set'); + } + } +} + +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace IoBrokerContextBehavior { + export class State { + adapter!: MatterAdapter; + device!: GenericDevice; + } +} diff --git a/src/matter/to-iobroker/GenericDeviceToIoBroker.ts b/src/matter/to-iobroker/GenericDeviceToIoBroker.ts index 09c27b33..2073e210 100644 --- a/src/matter/to-iobroker/GenericDeviceToIoBroker.ts +++ b/src/matter/to-iobroker/GenericDeviceToIoBroker.ts @@ -103,6 +103,14 @@ export abstract class GenericDeviceToIoBroker { /** Return the ioBroker device this mapping is for. */ abstract ioBrokerDevice: GenericDevice; + get ioBrokerDeviceType(): string | undefined { + return this.ioBrokerDevice.deviceType; + } + + get iconDeviceType(): string | undefined { + return this.ioBrokerDevice.deviceType; + } + get name(): string { return this.#name ?? this.#defaultName ?? this.deviceType; } @@ -666,13 +674,13 @@ export abstract class GenericDeviceToIoBroker { powerSource.isAttributeSupportedByName('batQuantity') && powerSource.isAttributeSupportedByName('batReplacementDescription') ) { - states.includedBattery = `${await powerSource.getBatQuantityAttribute()} x ${await powerSource.getBatReplacementDescriptionAttribute()}`; + states.includedBattery = `${await powerSource.getBatQuantityAttribute(false)} x ${await powerSource.getBatReplacementDescriptionAttribute(false)}`; } const voltage = powerSource.isAttributeSupportedByName('batVoltage') - ? await powerSource.getBatVoltageAttribute() + ? await powerSource.getBatVoltageAttribute(false) : undefined; const percentRemaining = powerSource.isAttributeSupportedByName('batPercentRemaining') - ? await powerSource.getBatPercentRemainingAttribute() + ? await powerSource.getBatPercentRemainingAttribute(false) : undefined; if (typeof voltage === 'number') { @@ -689,7 +697,7 @@ export abstract class GenericDeviceToIoBroker { return states; } - async getDeviceDetails(): Promise { + async getDeviceDetails(nodeConnected: boolean): Promise { const result: StructuredJsonFormData = {}; const states = this.ioBrokerDevice.getStates(); @@ -709,7 +717,7 @@ export abstract class GenericDeviceToIoBroker { .map(({ name, code }) => `${name} (${toHex(code)})`) .join(', '), endpoint: this.appEndpoint.number, - ...(await this.getMatterStates()), + ...(nodeConnected ? await this.getMatterStates() : {}), } as Record; result.matterClusters = {} as Record; diff --git a/src/matter/to-iobroker/UtilityOnlyToIoBroker.ts b/src/matter/to-iobroker/UtilityOnlyToIoBroker.ts index 5aed3d66..6366874d 100644 --- a/src/matter/to-iobroker/UtilityOnlyToIoBroker.ts +++ b/src/matter/to-iobroker/UtilityOnlyToIoBroker.ts @@ -1,5 +1,6 @@ import ChannelDetector from '@iobroker/type-detector'; import type { Endpoint, PairedNode } from '@project-chip/matter.js/device'; +import { PowerSource } from '@matter/main/clusters'; import type { GenericDevice } from '../../lib'; import type ElectricityDataDevice from '../../lib/devices/ElectricityDataDevice'; import type { DetectedDevice } from '../../lib/devices/GenericDevice'; @@ -43,12 +44,54 @@ export class UtilityOnlyToIoBroker extends GenericElectricityDataDeviceToIoBroke ); } + override get ioBrokerDeviceType(): string | undefined { + return 'ElectricityDataDevice'; + } + + override get iconDeviceType(): string | undefined { + switch (this.deviceType) { + case 'ElectricalSensor': + // Electrical sensor icon + return 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+DQogICAgPHBhdGggZmlsbD0iY3VycmVudENvbG9yIiBkPSJtMjIgMjAuNTktNC42OS00LjY5QzE4LjM3IDE0LjU1IDE5IDEyLjg1IDE5IDExYzAtNC40Mi0zLjU4LTgtOC04LTQuMDggMC03LjQ0IDMuMDUtNy45MyA3aDIuMDJDNS41NyA3LjE3IDguMDMgNSAxMSA1YzMuMzEgMCA2IDIuNjkgNiA2cy0yLjY5IDYtNiA2Yy0yLjQyIDAtNC41LTEuNDQtNS40NS0zLjVIMy40QzQuNDUgMTYuNjkgNy40NiAxOSAxMSAxOWMxLjg1IDAgMy41NS0uNjMgNC45LTEuNjlMMjAuNTkgMjJ6IiAvPg0KICAgIDxwYXRoIGZpbGw9ImN1cnJlbnRDb2xvciIgZD0iTTguNDMgOS42OSA5LjY1IDE1aDEuNjRsMS4yNi0zLjc4Ljk1IDIuMjhoMlYxMmgtMWwtMS4yNS0zaC0xLjU0bC0xLjEyIDMuMzdMOS4zNSA3SDcuN2wtMS4yNSA0SDF2MS41aDYuNTV6IiAvPg0KPC9zdmc+'; + case 'BridgedNode': + return 'node'; + case 'PowerSource': { + const powerSource = this.appEndpoint.getClusterClient(PowerSource.Complete); + if (powerSource) { + if ( + powerSource.supportedFeatures.battery || + powerSource.isAttributeSupportedByName('batChargeLevel') + ) { + // Battery icon + return 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+DQogICAgPHBhdGggZmlsbD0iY3VycmVudENvbG9yIiBkPSJNMTUuNjcgNEgxNFYyaC00djJIOC4zM0M3LjYgNCA3IDQuNiA3IDUuMzN2MTUuMzNDNyAyMS40IDcuNiAyMiA4LjMzIDIyaDcuMzNjLjc0IDAgMS4zNC0uNiAxLjM0LTEuMzNWNS4zM0MxNyA0LjYgMTYuNCA0IDE1LjY3IDQiIC8+DQo8L3N2Zz4='; + } else if ( + powerSource.supportedFeatures.wired || + powerSource.isAttributeSupportedByName('wiredCurrentType') + ) { + // Wired icon + return 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+DQogICAgPHBhdGggZmlsbD0iY3VycmVudENvbG9yIiBkPSJNMTYuMDEgNyAxNiAzaC0ydjRoLTRWM0g4djRoLS4wMUM3IDYuOTkgNiA3Ljk5IDYgOC45OXY1LjQ5TDkuNSAxOHYzaDV2LTNsMy41LTMuNTF2LTUuNWMwLTEtMS0yLTEuOTktMS45OSIgLz4NCjwvc3ZnPg=='; + } + } + } + // eslint-disable-next-line no-fallthrough + default: + if (this.#deviceTypeSupported) { + const icon = super.iconDeviceType; + if (icon) { + return icon; + } + } + // Questionmark icon + return 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgPg0KICAgIDxwYXRoIGZpbGw9ImN1cnJlbnRDb2xvciIgZD0iTTExLjA3IDEyLjg1Yy43Ny0xLjM5IDIuMjUtMi4yMSAzLjExLTMuNDQuOTEtMS4yOS40LTMuNy0yLjE4LTMuNy0xLjY5IDAtMi41MiAxLjI4LTIuODcgMi4zNEw2LjU0IDYuOTZDNy4yNSA0LjgzIDkuMTggMyAxMS45OSAzYzIuMzUgMCAzLjk2IDEuMDcgNC43OCAyLjQxLjcgMS4xNSAxLjExIDMuMy4wMyA0LjktMS4yIDEuNzctMi4zNSAyLjMxLTIuOTcgMy40NS0uMjUuNDYtLjM1Ljc2LS4zNSAyLjI0aC0yLjg5Yy0uMDEtLjc4LS4xMy0yLjA1LjQ4LTMuMTVNMTQgMjBjMCAxLjEtLjkgMi0yIDJzLTItLjktMi0yIC45LTIgMi0yIDIgLjkgMiAyIj48L3BhdGg+DQo8L3N2Zz4='; + } + } + get ioBrokerDevice(): GenericDevice { return this.#ioBrokerDevice; } - override async getDeviceDetails(): Promise { - const details = await super.getDeviceDetails(); + override async getDeviceDetails(nodeConnected: boolean): Promise { + const details = await super.getDeviceDetails(nodeConnected); const unsupportedInfo = { __header__UnsupportedNotice: 'This Device type is not automatically mapped to ioBroker!', diff --git a/src/matter/to-iobroker/WindowCoveringToIoBroker.ts b/src/matter/to-iobroker/WindowCoveringToIoBroker.ts index 4ae565a2..7c0e90b5 100644 --- a/src/matter/to-iobroker/WindowCoveringToIoBroker.ts +++ b/src/matter/to-iobroker/WindowCoveringToIoBroker.ts @@ -112,12 +112,13 @@ export class WindowCoveringToIoBroker extends GenericElectricityDataDeviceToIoBr convertValue: (value: TypeFromBitSchema) => value.global !== WindowCovering.MovementStatus.Stopped, }); + // TODO introduce value.direction once in type definition /*this.enableDeviceTypeStateForAttribute(PropertyType.Direction, { endpointId: this.appEndpoint.getNumber(), clusterId: WindowCovering.Cluster.id, attributeName: 'configStatus', convertValue: (value: TypeFromBitSchema) => { - return !value.global !== WindowCovering.MovementStatus.Stopped; + return value.global !== WindowCovering.MovementStatus.Stopped; }, });*/