From effe0171d981493515bc3ae06aac3a00b9ca46dd Mon Sep 17 00:00:00 2001 From: Luligu Date: Fri, 22 Nov 2024 13:24:05 +0100 Subject: [PATCH 01/17] Dev 2.2.2-dev.1 --- .gitignore | 3 +- CHANGELOG.md | 14 + TODO.md | 0 matterbridge-zigbee2mqtt.schema.json | 8 +- package-lock.json | 1020 ++++++++------------------ package.json | 32 +- src/entity.ts | 35 +- src/index.test.ts | 2 +- src/platform.test.ts | 2 +- src/platform.ts | 28 +- 10 files changed, 364 insertions(+), 780 deletions(-) delete mode 100644 TODO.md diff --git a/.gitignore b/.gitignore index af69613..6024f0b 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,5 @@ replay_pid* # zigbee2mqtt bridge-info.json bridge-devices.json -bridge-groups.json \ No newline at end of file +bridge-groups.json +TODO.md \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 89c2255..1af6e38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ If you like this project and find it useful, please consider giving it a star on All notable changes to this project will be documented in this file. +## [2.2.2] - 2024-10-22 + +### Changed + +- [package]: Updated dependencies. +- [plugin]: Require Matterbridge 1.6.0. + +### Fixed + + + + Buy me a coffee + + ## [2.2.1] - 2024-10-11 ### Fixed diff --git a/TODO.md b/TODO.md deleted file mode 100644 index e69de29..0000000 diff --git a/matterbridge-zigbee2mqtt.schema.json b/matterbridge-zigbee2mqtt.schema.json index c507444..4322620 100644 --- a/matterbridge-zigbee2mqtt.schema.json +++ b/matterbridge-zigbee2mqtt.schema.json @@ -2,11 +2,7 @@ "title": "Matterbridge zigbee2mqtt plugin", "description": "matterbridge-zigbee2mqtt v. 2.0.17 by https://github.com/Luligu", "type": "object", - "required": [ - "host", - "port", - "topic" - ], + "required": ["host", "port", "topic"], "properties": { "name": { "description": "Plugin name", @@ -113,4 +109,4 @@ "default": false } } -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index 922e97d..290cc83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,35 +1,33 @@ { "name": "matterbridge-zigbee2mqtt", - "version": "2.2.1", + "version": "2.2.2-dev.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "matterbridge-zigbee2mqtt", - "version": "2.2.1", - "hasInstallScript": true, + "version": "2.2.2-dev.1", "license": "Apache-2.0", "dependencies": { "moment": "2.30.1", - "mqtt": "5.10.1", + "mqtt": "5.10.2", "node-ansi-logger": "3.0.0", "node-persist-manager": "1.0.8" }, "devDependencies": { - "@eslint/js": "9.12.0", + "@eslint/js": "9.15.0", "@types/eslint__js": "8.42.3", - "@types/jest": "29.5.13", - "@types/node": "22.7.5", - "eslint": "9.12.0", + "@types/jest": "29.5.14", + "@types/node": "22.9.1", + "eslint": "9.15.0", "eslint-config-prettier": "9.1.0", - "eslint-plugin-jest": "28.8.3", + "eslint-plugin-jest": "28.9.0", "eslint-plugin-prettier": "5.2.1", "jest": "29.7.0", "prettier": "3.3.3", - "rimraf": "6.0.1", "ts-jest": "29.2.5", "typescript": "5.6.3", - "typescript-eslint": "8.8.1" + "typescript-eslint": "8.15.0" }, "engines": { "node": ">=18.0.0" @@ -54,13 +52,14 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", - "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/highlight": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { @@ -68,9 +67,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.8.tgz", - "integrity": "sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", + "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", "dev": true, "license": "MIT", "engines": { @@ -78,22 +77,22 @@ } }, "node_modules/@babel/core": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.8.tgz", - "integrity": "sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helpers": "^7.25.7", - "@babel/parser": "^7.25.8", - "@babel/template": "^7.25.7", - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.8", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -119,13 +118,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", - "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.7", + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -135,14 +135,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", - "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.7", - "@babel/helper-validator-option": "^7.25.7", + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -162,30 +162,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", - "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", - "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.7", - "@babel/helper-simple-access": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -195,33 +194,19 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", - "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", - "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", - "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true, "license": "MIT", "engines": { @@ -229,9 +214,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", - "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "dev": true, "license": "MIT", "engines": { @@ -239,9 +224,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", - "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "dev": true, "license": "MIT", "engines": { @@ -249,121 +234,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", - "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", - "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/parser": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz", - "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.8" + "@babel/types": "^7.26.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -428,13 +319,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.7.tgz", - "integrity": "sha512-AqVo+dguCgmpi/3mYBdu9lkngOBlQ2w2vnNpa6gfiCxQZLzV4ZbhsXitJ2Yblkoe1VQwtHSaNmIaGll/26YWRw==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -470,13 +361,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.7.tgz", - "integrity": "sha512-ruZOnKO+ajVL/MVx+PwNBPOkrnXTXoWMtte1MBpegfCArhqOe3Bj52avVj1huLLxNKYKXYaSxZ2F+woK1ekXfw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -596,13 +487,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.7.tgz", - "integrity": "sha512-rR+5FDjpCHqqZN2bzZm18bVYGaejGq5ZkpVCJLXor/+zlSrSoc4KWcHI0URVWjl/68Dyr1uwZUz/1njycEAv9g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -612,9 +503,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", - "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -624,32 +515,32 @@ } }, "node_modules/@babel/template": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", - "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", - "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7", + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -668,15 +559,14 @@ } }, "node_modules/@babel/types": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz", - "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -690,17 +580,20 @@ "license": "MIT" }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } @@ -719,9 +612,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", - "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, "license": "MIT", "engines": { @@ -729,9 +622,9 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", - "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.0.tgz", + "integrity": "sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -744,9 +637,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.6.0.tgz", - "integrity": "sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.0.tgz", + "integrity": "sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg==", "dev": true, "license": "Apache-2.0", "engines": { @@ -754,9 +647,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", - "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", "dev": true, "license": "MIT", "dependencies": { @@ -778,9 +671,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.12.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.12.0.tgz", - "integrity": "sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==", + "version": "9.15.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.15.0.tgz", + "integrity": "sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==", "dev": true, "license": "MIT", "engines": { @@ -798,9 +691,9 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz", - "integrity": "sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz", + "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -811,9 +704,9 @@ } }, "node_modules/@humanfs/core": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz", - "integrity": "sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -821,34 +714,20 @@ } }, "node_modules/@humanfs/node": { - "version": "0.16.5", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.5.tgz", - "integrity": "sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==", + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.0", + "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" }, "engines": { "node": ">=18.18.0" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", @@ -862,107 +741,32 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, + "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": ">=12.22" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "node_modules/@humanwhocodes/retry": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, + "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": ">=18.18" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, "node_modules/@istanbuljs/load-nyc-config": { @@ -1616,9 +1420,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.13", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.13.tgz", - "integrity": "sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg==", + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1634,18 +1438,18 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", - "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "version": "22.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.1.tgz", + "integrity": "sha512-p8Yy/8sw1caA8CdRIQBG5tiLHmxtQKObCijiAa9Ez+d4+PRffM4054xbju0msf+cvhJpnFEeNjxmVT/0ipktrg==", "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.19.8" } }, "node_modules/@types/readable-stream": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.15.tgz", - "integrity": "sha512-oAZ3kw+kJFkEqyh7xORZOku1YAKvsFTogRY8kVl4vHpEKiDkfnSA/My8haRE7fvmix5Zyy+1pwzOi7yycGLBJw==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.18.tgz", + "integrity": "sha512-21jK/1j+Wg+7jVw1xnSwy/2Q1VgVjWuFssbYGTREPUBeZ+rqVFl2udq0IkxzPC0ZhOzVceUbyIACFZKLqKEBlA==", "license": "MIT", "dependencies": { "@types/node": "*", @@ -1660,9 +1464,9 @@ "license": "MIT" }, "node_modules/@types/ws": { - "version": "8.5.12", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", - "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", + "version": "8.5.13", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", + "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", "license": "MIT", "dependencies": { "@types/node": "*" @@ -1686,17 +1490,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.1.tgz", - "integrity": "sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.15.0.tgz", + "integrity": "sha512-+zkm9AR1Ds9uLWN3fkoeXgFppaQ+uEVtfOV62dDmsy9QCNqlRHWNEck4yarvRNrvRcHQLGfqBNui3cimoz8XAg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/type-utils": "8.8.1", - "@typescript-eslint/utils": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/scope-manager": "8.15.0", + "@typescript-eslint/type-utils": "8.15.0", + "@typescript-eslint/utils": "8.15.0", + "@typescript-eslint/visitor-keys": "8.15.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1720,16 +1524,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.8.1.tgz", - "integrity": "sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.15.0.tgz", + "integrity": "sha512-7n59qFpghG4uazrF9qtGKBZXn7Oz4sOMm8dwNWDQY96Xlm2oX67eipqcblDj+oY1lLCbf1oltMZFpUso66Kl1A==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/typescript-estree": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/scope-manager": "8.15.0", + "@typescript-eslint/types": "8.15.0", + "@typescript-eslint/typescript-estree": "8.15.0", + "@typescript-eslint/visitor-keys": "8.15.0", "debug": "^4.3.4" }, "engines": { @@ -1749,14 +1553,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.1.tgz", - "integrity": "sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.15.0.tgz", + "integrity": "sha512-QRGy8ADi4J7ii95xz4UoiymmmMd/zuy9azCaamnZ3FM8T5fZcex8UfJcjkiEZjJSztKfEBe3dZ5T/5RHAmw2mA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1" + "@typescript-eslint/types": "8.15.0", + "@typescript-eslint/visitor-keys": "8.15.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1767,14 +1571,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.8.1.tgz", - "integrity": "sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.15.0.tgz", + "integrity": "sha512-UU6uwXDoI3JGSXmcdnP5d8Fffa2KayOhUUqr/AiBnG1Gl7+7ut/oyagVeSkh7bxQ0zSXV9ptRh/4N15nkCqnpw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.8.1", - "@typescript-eslint/utils": "8.8.1", + "@typescript-eslint/typescript-estree": "8.15.0", + "@typescript-eslint/utils": "8.15.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1785,6 +1589,9 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, "peerDependenciesMeta": { "typescript": { "optional": true @@ -1792,9 +1599,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.1.tgz", - "integrity": "sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.15.0.tgz", + "integrity": "sha512-n3Gt8Y/KyJNe0S3yDCD2RVKrHBC4gTUcLTebVBXacPy091E6tNspFLKRXlk3hwT4G55nfr1n2AdFqi/XMxzmPQ==", "dev": true, "license": "MIT", "engines": { @@ -1806,14 +1613,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.1.tgz", - "integrity": "sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.15.0.tgz", + "integrity": "sha512-1eMp2JgNec/niZsR7ioFBlsh/Fk0oJbhaqO0jRyQBMgkz7RrFfkqF9lYYmBoGBaSiLnu8TAPQTwoTUiSTUW9dg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/types": "8.15.0", + "@typescript-eslint/visitor-keys": "8.15.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1861,16 +1668,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.1.tgz", - "integrity": "sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.15.0.tgz", + "integrity": "sha512-k82RI9yGhr0QM3Dnq+egEpz9qB6Un+WLYhmoNcvl8ltMEededhh7otBVVIDDsEEttauwdY/hQoSsOv13lxrFzQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/typescript-estree": "8.8.1" + "@typescript-eslint/scope-manager": "8.15.0", + "@typescript-eslint/types": "8.15.0", + "@typescript-eslint/typescript-estree": "8.15.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1881,17 +1688,22 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.1.tgz", - "integrity": "sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.15.0.tgz", + "integrity": "sha512-h8vYOulWec9LhpwfAdZf2bjr8xIp0KNKnpgqSz0qqYYKAW/QZKw3ktRndbiAtUz4acH4QLQavwZBYCc0wulA/Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.8.1", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "8.15.0", + "eslint-visitor-keys": "^4.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1901,19 +1713,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -1927,9 +1726,9 @@ } }, "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "license": "MIT", "bin": { @@ -2226,9 +2025,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", - "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "dev": true, "funding": [ { @@ -2246,10 +2045,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001663", - "electron-to-chromium": "^1.5.28", + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -2332,9 +2131,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001668", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001668.tgz", - "integrity": "sha512-nWLrdxqCdblixUO+27JtGJJE/txpJlyUy5YN1u53wLZkP0emYCo5zgS6QYft7VUYR42LGgi/S5hdLZTrnyIddw==", + "version": "1.0.30001683", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001683.tgz", + "integrity": "sha512-iqmNnThZ0n70mNwvxpEC2nBJ037ZHZUoBI5Gorh1Mw6IlEAZujEoU1tXA628iZfzm7R9FvFzxbfdgml82a3k8Q==", "dev": true, "funding": [ { @@ -2527,9 +2326,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -2610,13 +2409,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -2634,9 +2426,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.36", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.36.tgz", - "integrity": "sha512-HYTX8tKge/VNp6FGO+f/uVDmUkq+cEfcxYhKf15Akc4M5yxt5YmorwlAitKWjWhWQnKcDRBAQKXkhqqXMqcrjw==", + "version": "1.5.64", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.64.tgz", + "integrity": "sha512-IXEuxU+5ClW2IGEYFC2T7szbyVgehupCWQe5GNh+H065CD6U6IFN0s4KeAMFGNmQolRU4IV7zGBWSYMmZ8uuqQ==", "dev": true, "license": "ISC" }, @@ -2694,32 +2486,32 @@ } }, "node_modules/eslint": { - "version": "9.12.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.12.0.tgz", - "integrity": "sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==", + "version": "9.15.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.15.0.tgz", + "integrity": "sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.11.0", - "@eslint/config-array": "^0.18.0", - "@eslint/core": "^0.6.0", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.12.0", - "@eslint/plugin-kit": "^0.2.0", - "@humanfs/node": "^0.16.5", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.9.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.15.0", + "@eslint/plugin-kit": "^0.2.3", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.3.1", + "@humanwhocodes/retry": "^0.4.1", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.5", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.1.0", - "eslint-visitor-keys": "^4.1.0", - "espree": "^10.2.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -2733,8 +2525,7 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" @@ -2768,9 +2559,9 @@ } }, "node_modules/eslint-plugin-jest": { - "version": "28.8.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.8.3.tgz", - "integrity": "sha512-HIQ3t9hASLKm2IhIOqnu+ifw7uLZkIlR7RYNv7fMcEi/p0CIiJmfriStQS2LDkgtY4nyLbIZAD+JL347Yc2ETQ==", + "version": "28.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.9.0.tgz", + "integrity": "sha512-rLu1s1Wf96TgUUxSw6loVIkNtUjq1Re7A9QdCCHSohnvXEBAjuL420h0T/fMmkQlNsQP2GhQzEUpYHPfxBkvYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2825,9 +2616,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz", - "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -2842,9 +2633,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", - "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2855,15 +2646,15 @@ } }, "node_modules/espree": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", - "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.12.0", + "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.1.0" + "eslint-visitor-keys": "^4.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3182,42 +2973,12 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "dev": true, "license": "ISC" }, - "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3663,22 +3424,6 @@ "node": ">=8" } }, - "node_modules/jackspeak": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", - "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/jake": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", @@ -4552,16 +4297,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/moment": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", @@ -4572,9 +4307,9 @@ } }, "node_modules/mqtt": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.10.1.tgz", - "integrity": "sha512-hXCOki8sANoQ7w+2OzJzg6qMBxTtrH9RlnVNV8panLZgnl+Gh0J/t4k6r8Az8+C7y3KAcyXtn0mmLixyUom8Sw==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.10.2.tgz", + "integrity": "sha512-Q8NrMXB6FwQ2DulGONeDb6BtFHxyQHmXWzDrSC724iyofxLleq/wuZmztV3kg1Kda9I7l0oHP+FKesowoFxyUg==", "license": "MIT", "dependencies": { "@types/readable-stream": "^4.0.5", @@ -4585,7 +4320,7 @@ "help-me": "^5.0.0", "lru-cache": "^10.0.1", "minimist": "^1.2.8", - "mqtt-packet": "^9.0.0", + "mqtt-packet": "^9.0.1", "number-allocator": "^1.0.14", "readable-stream": "^4.4.2", "reinterval": "^1.1.0", @@ -4604,9 +4339,9 @@ } }, "node_modules/mqtt-packet": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.0.tgz", - "integrity": "sha512-8v+HkX+fwbodsWAZIZTI074XIoxVBOmPeggQuDFCGg1SqNcC+uoRMWu7J6QlJPqIUIJXmjNYYHxBBLr1Y/Df4w==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.1.tgz", + "integrity": "sha512-koZF1V/X2RZUI6uD9wN5OK1JxxcG1ofAR4H3LjCw1FkeKzruZQ26aAA6v2m1lZyWONZIR5wMMJFrZJDRNzbiQw==", "license": "MIT", "dependencies": { "bl": "^6.0.8", @@ -4804,13 +4539,6 @@ "node": ">=6" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4880,37 +4608,10 @@ "dev": true, "license": "MIT" }, - "node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.1.tgz", - "integrity": "sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "20 || >=22" - } - }, "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, @@ -5273,76 +4974,6 @@ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "license": "MIT" }, - "node_modules/rimraf": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", - "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^11.0.0", - "package-json-from-dist": "^1.0.0" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", - "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -5551,22 +5182,6 @@ "node": ">=8" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -5580,20 +5195,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -5685,13 +5286,6 @@ "node": ">=8" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "license": "MIT" - }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -5699,16 +5293,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -5723,9 +5307,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", + "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", "dev": true, "license": "MIT", "engines": { @@ -5785,9 +5369,9 @@ } }, "node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, "node_modules/type-check": { @@ -5847,15 +5431,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.8.1.tgz", - "integrity": "sha512-R0dsXFt6t4SAFjUSKFjMh4pXDtq04SsFKCVGDP3ZOzNP7itF0jBcZYU4fMsZr4y7O7V7Nc751dDeESbe4PbQMQ==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.15.0.tgz", + "integrity": "sha512-wY4FRGl0ZI+ZU4Jo/yjdBu0lVTSML58pu6PgGtJmCufvzfV565pUF6iACQt092uFOd49iLOTX/sEVmHtbSrS+w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.8.1", - "@typescript-eslint/parser": "8.8.1", - "@typescript-eslint/utils": "8.8.1" + "@typescript-eslint/eslint-plugin": "8.15.0", + "@typescript-eslint/parser": "8.15.0", + "@typescript-eslint/utils": "8.15.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5864,6 +5448,9 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, "peerDependenciesMeta": { "typescript": { "optional": true @@ -6026,25 +5613,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index c87b43b..229fd10 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matterbridge-zigbee2mqtt", - "version": "2.2.1", + "version": "2.2.2-dev.1", "description": "Matterbridge zigbee2mqtt plugin", "author": "https://github.com/Luligu", "license": "Apache-2.0", @@ -55,14 +55,14 @@ "lint:fix": "eslint --fix --max-warnings=0 .", "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,md}\"", "format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json,css,md}\"", - "clean": "npm install --no-save rimraf && rimraf tsconfig.tsbuildinfo ./dist", + "clean": "npx rimraf tsconfig.tsbuildinfo ./dist", "cleanBuild": "npm run clean && tsc", - "deepClean": "npm install --no-save rimraf && rimraf tsconfig.tsbuildinfo package-lock.json npm-shrinkwrap.json ./dist ./node_modules", + "deepClean": "npx rimraf tsconfig.tsbuildinfo package-lock.json npm-shrinkwrap.json ./dist ./node_modules", "deepCleanBuild": "npm run deepClean && npm install && npm link matterbridge && npm run build", "checkDependencies": "npx npm-check-updates", - "updateDependencies": "npx npm-check-updates -u && npm install && npm link matterbridge && npm run cleanBuild", - "prepublishOnly": "npm shrinkwrap", - "npmPack": "npm run deepCleanBuild && npm shrinkwrap && npm pack && npm run deepCleanBuild", + "updateDependencies": "npx npm-check-updates -u && npm install && npm link matterbridge && npm run build", + "prepublishOnly": "npm pkg delete devDependencies && npm install --omit=dev && npm shrinkwrap", + "npmPack": "npm run cleanBuild && copy package.json package.log && npm pkg delete devDependencies && npm install --omit=dev && npm shrinkwrap && npm pack && copy package.log package.json && npm run deepCleanBuild", "matterbridge:add": "matterbridge -add .\\", "matterbridge:remove": "matterbridge -remove .\\", "matterbridge:enable": "matterbridge -enable .\\", @@ -70,10 +70,7 @@ "matterbridge:list": "matterbridge -list", "dev:link": "npm link matterbridge", "dev:unlink": "npm unlink matterbridge", - "dev:install": "npm install --no-save matterbridge", - "dev:uninstall": "npm uninstall matterbridge", - "install": "node link-matterbridge-script.js", - "install:dependencies": "npm install node-ansi-logger moment mqtt && npm install --save-dev rimraf", + "install:dependencies": "npm install node-ansi-logger moment mqtt", "install:typescript": "npm install --save-dev typescript @types/node && npm run install && npm run build", "install:eslint": "npm install --save-dev eslint @eslint/js @types/eslint__js typescript typescript-eslint", "install:prettier": "npm install --save-dev prettier eslint-config-prettier eslint-plugin-prettier", @@ -81,24 +78,23 @@ }, "dependencies": { "moment": "2.30.1", - "mqtt": "5.10.1", + "mqtt": "5.10.2", "node-ansi-logger": "3.0.0", "node-persist-manager": "1.0.8" }, "devDependencies": { - "@eslint/js": "9.12.0", + "@eslint/js": "9.15.0", "@types/eslint__js": "8.42.3", - "@types/jest": "29.5.13", - "@types/node": "22.7.5", - "eslint": "9.12.0", + "@types/jest": "29.5.14", + "@types/node": "22.9.1", + "eslint": "9.15.0", "eslint-config-prettier": "9.1.0", - "eslint-plugin-jest": "28.8.3", + "eslint-plugin-jest": "28.9.0", "eslint-plugin-prettier": "5.2.1", "jest": "29.7.0", "prettier": "3.3.3", - "rimraf": "6.0.1", "ts-jest": "29.2.5", "typescript": "5.6.3", - "typescript-eslint": "8.8.1" + "typescript-eslint": "8.15.0" } } \ No newline at end of file diff --git a/src/entity.ts b/src/entity.ts index f5e81eb..86bd3d1 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -68,6 +68,10 @@ import { dimmableLight, colorTemperatureLight, onOffOutlet, + DescriptorCluster, + Descriptor, + ClusterServer, + VendorId, } from 'matterbridge'; import { AnsiLogger, TimestampFormat, gn, dn, ign, idn, rs, db, wr, debugStringify, hk, zb, or, nf, LogLevel, CYAN } from 'matterbridge/logger'; import { deepCopy, deepEqual } from 'matterbridge/utils'; @@ -764,7 +768,11 @@ export class ZigbeeDevice extends ZigbeeEntity { else this.bridgedDevice.addDeviceTypeWithClusterServer([z2m.deviceType], [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)]); } else { if (!this.bridgedDevice) this.bridgedDevice = new BridgedBaseDevice(this, [bridgedNode]); - this.bridgedDevice.addChildDeviceTypeWithClusterServer(endpoint, [z2m.deviceType], [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)]); + /* const child = */ this.bridgedDevice.addChildDeviceTypeWithClusterServer(endpoint, [z2m.deviceType], [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)]); + // if (endpoint === 'l1') addTagList(child, 0x07, 1, 'endpoint ' + endpoint); + // if (endpoint === 'l2') addTagList(child, 0x07, 2, 'endpoint ' + endpoint); + // if (endpoint === 'l1') addTagList(child, null, 0x43, 0x08, 'endpoint ' + endpoint); + // if (endpoint === 'l2') addTagList(child, null, 0x43, 0x08, 'endpoint ' + endpoint); this.bridgedDevice.addFixedLabel('composed', type); this.bridgedDevice.hasEndpoints = true; } @@ -1090,6 +1098,31 @@ export class ZigbeeDevice extends ZigbeeEntity { } } +export function addTagList(endpoint: Endpoint, mfgCode: VendorId | null, namespaceId: number, tag: number, label: string | null = null) { + const descriptor = endpoint.getClusterServerById(DescriptorCluster.id); + if (!descriptor) return; + // console.log('addTagList', namespaceId, tag, label); + // console.log('original descriptor', descriptor); + + endpoint.addClusterServer( + ClusterServer( + DescriptorCluster.with(Descriptor.Feature.TagList), + { + tagList: [{ mfgCode, namespaceId, tag, label }], + deviceTypeList: [...descriptor.attributes.deviceTypeList.getLocal()], + serverList: [...descriptor.attributes.serverList.getLocal()], + clientList: [...descriptor.attributes.clientList.getLocal()], + partsList: [...descriptor.attributes.partsList.getLocal()], + }, + {}, + {}, + ), + ); + + // descriptor = endpoint.getClusterServerById(DescriptorCluster.id); + // console.log('new descriptor', descriptor); +} + export class BridgedBaseDevice extends MatterbridgeDevice { public hasEndpoints = false; public isRouter = false; diff --git a/src/index.test.ts b/src/index.test.ts index e754845..f165f9e 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -15,7 +15,7 @@ describe('initializePlugin', () => { matterbridgeDirectory: '', matterbridgePluginDirectory: 'temp', systemInformation: { ipv4Address: undefined }, - matterbridgeVersion: '1.5.5', + matterbridgeVersion: '1.6.0', removeAllBridgedDevices: jest.fn(), } as unknown as Matterbridge; mockLog = { error: jest.fn(), warn: jest.fn(), info: jest.fn(), debug: jest.fn() } as unknown as AnsiLogger; diff --git a/src/platform.test.ts b/src/platform.test.ts index 43a85a4..25bc159 100644 --- a/src/platform.test.ts +++ b/src/platform.test.ts @@ -18,7 +18,7 @@ describe('TestPlatform', () => { matterbridgeDirectory: '', matterbridgePluginDirectory: 'temp', systemInformation: { ipv4Address: undefined }, - matterbridgeVersion: '1.5.5', + matterbridgeVersion: '1.6.0', removeAllBridgedDevices: jest.fn(), } as unknown as Matterbridge; mockLog = { error: jest.fn(), warn: jest.fn(), info: jest.fn(), debug: jest.fn() } as unknown as AnsiLogger; diff --git a/src/platform.ts b/src/platform.ts index f4d18b1..d05fe92 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -78,8 +78,8 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { super(matterbridge, log, config); // Verify that Matterbridge is the correct version - if (!this.localVerifyMatterbridgeVersion('1.5.5')) { - throw new Error(`The zigbee2mqtt plugin requires Matterbridge version >= "1.5.5". Please update Matterbridge to the latest version in the frontend."`); + if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('1.6.0')) { + throw new Error(`This plugin requires Matterbridge version >= "1.6.0". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend."`); } this.debugEnabled = config.debug as boolean; @@ -430,30 +430,6 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { this.publishCallBack = undefined; } - localVerifyMatterbridgeVersion(requiredVersion: string): boolean { - const compareVersions = (matterbridgeVersion: string, requiredVersion: string): boolean => { - const stripTag = (v: string) => { - const parts = v.split('-'); - return parts.length > 0 ? parts[0] : v; - }; - const v1Parts = stripTag(matterbridgeVersion).split('.').map(Number); - const v2Parts = stripTag(requiredVersion).split('.').map(Number); - for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) { - const v1Part = v1Parts[i] || 0; - const v2Part = v2Parts[i] || 0; - if (v1Part < v2Part) { - return false; - } else if (v1Part > v2Part) { - return true; - } - } - return true; - }; - - if (!compareVersions(this.matterbridge.matterbridgeVersion, requiredVersion)) return false; - return true; - } - /** * @deprecated */ From 24420358611fb33bcb94305def26a440c70d703a Mon Sep 17 00:00:00 2001 From: Luligu Date: Fri, 22 Nov 2024 13:40:55 +0100 Subject: [PATCH 02/17] Dev 2.2.2-dev.1 --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9636dca..acdf26e 100644 --- a/README.md +++ b/README.md @@ -28,13 +28,13 @@ Follow these steps to install or update Matterbridge if it is not already instal on Windows: ``` -npm install -g matterbridge +npm install -g matterbridge --omit=dev ``` on Linux (you need the necessary permissions): ``` -sudo npm install -g matterbridge +sudo npm install -g matterbridge --omit=dev ``` See the complete guidelines on [Matterbridge](https://github.com/Luligu/matterbridge/blob/main/README.md) for more information. @@ -57,7 +57,7 @@ On windows: ``` cd $HOME\Matterbridge -npm install -g matterbridge-zigbee2mqtt +npm install -g matterbridge-zigbee2mqtt --omit=dev matterbridge -add matterbridge-zigbee2mqtt ``` @@ -65,7 +65,7 @@ On linux: ``` cd ~/Matterbridge -sudo npm install -g matterbridge-zigbee2mqtt +sudo npm install -g matterbridge-zigbee2mqtt --omit=dev matterbridge -add matterbridge-zigbee2mqtt ``` @@ -84,6 +84,7 @@ cd $HOME\Matterbridge git clone https://github.com/Luligu/matterbridge-zigbee2mqtt cd matterbridge-zigbee2mqtt npm install +npm run dev:link npm run build matterbridge -add .\ ``` From 48a80ed7151aacb8c1f520eca2de68a84f2dbf4c Mon Sep 17 00:00:00 2001 From: Luligu Date: Fri, 22 Nov 2024 14:09:43 +0100 Subject: [PATCH 03/17] Fix occupied_heating_setpoint and occupied_cooling_setpoint --- CHANGELOG.md | 6 +++++- README.md | 5 +++-- package.json | 4 ++-- src/entity.ts | 26 ++++++++++++++++++++------ 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1af6e38..6e0e028 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,15 @@ All notable changes to this project will be documented in this file. ### Changed +- [readme]: Updated install script. +- [readme]: Updated build script. - [package]: Updated dependencies. -- [plugin]: Require Matterbridge 1.6.0. +- [plugin]: Requires Matterbridge 1.6.0. ### Fixed +- [thermostat]: Fixed the case when instead of current_heating_setpoint the property is occupied_heating_setpoint. +- [thermostat]: Fixed the case when instead of current_cooling_setpoint the property is occupied_cooling_setpoint. Buy me a coffee diff --git a/README.md b/README.md index acdf26e..efd81c9 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ On windows: cd $HOME\Matterbridge git clone https://github.com/Luligu/matterbridge-zigbee2mqtt cd matterbridge-zigbee2mqtt -npm install +npm ci npm run dev:link npm run build matterbridge -add .\ @@ -95,7 +95,8 @@ On linux: cd ~/Matterbridge git clone https://github.com/Luligu/matterbridge-zigbee2mqtt cd matterbridge-zigbee2mqtt -npm install +npm ci +npm run dev:link npm run build matterbridge -add ./ ``` diff --git a/package.json b/package.json index 229fd10..cb2a8af 100644 --- a/package.json +++ b/package.json @@ -61,8 +61,8 @@ "deepCleanBuild": "npm run deepClean && npm install && npm link matterbridge && npm run build", "checkDependencies": "npx npm-check-updates", "updateDependencies": "npx npm-check-updates -u && npm install && npm link matterbridge && npm run build", - "prepublishOnly": "npm pkg delete devDependencies && npm install --omit=dev && npm shrinkwrap", - "npmPack": "npm run cleanBuild && copy package.json package.log && npm pkg delete devDependencies && npm install --omit=dev && npm shrinkwrap && npm pack && copy package.log package.json && npm run deepCleanBuild", + "prepublishOnly": "npm pkg delete devDependencies && npm pkg delete scripts && npm install --omit=dev && npm shrinkwrap", + "npmPack": "npm run cleanBuild && copy package.json package.log && npm pkg delete devDependencies && npm pkg delete scripts && npm install --omit=dev && npm shrinkwrap && npm pack && copy package.log package.json && npm run deepCleanBuild", "matterbridge:add": "matterbridge -add .\\", "matterbridge:remove": "matterbridge -remove .\\", "matterbridge:enable": "matterbridge -enable .\\", diff --git a/src/entity.ts b/src/entity.ts index 86bd3d1..8390753 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -627,6 +627,8 @@ export const z2ms: ZigbeeToMatter[] = [ { type: 'climate', name: 'local_temperature', property: 'local_temperature', deviceType: DeviceTypes.THERMOSTAT, cluster: Thermostat.Cluster.id, attribute: 'localTemperature', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)) } }, { type: 'climate', name: 'current_heating_setpoint', property: 'current_heating_setpoint', deviceType: DeviceTypes.THERMOSTAT, cluster: Thermostat.Cluster.id, attribute: 'occupiedHeatingSetpoint', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)) } }, { type: 'climate', name: 'current_cooling_setpoint', property: 'current_cooling_setpoint', deviceType: DeviceTypes.THERMOSTAT, cluster: Thermostat.Cluster.id, attribute: 'occupiedCoolingSetpoint', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)) } }, + { type: 'climate', name: 'occupied_heating_setpoint', property: 'occupied_heating_setpoint', deviceType: DeviceTypes.THERMOSTAT, cluster: Thermostat.Cluster.id, attribute: 'occupiedHeatingSetpoint', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)) } }, + { type: 'climate', name: 'occupied_cooling_setpoint', property: 'occupied_cooling_setpoint', deviceType: DeviceTypes.THERMOSTAT, cluster: Thermostat.Cluster.id, attribute: 'occupiedCoolingSetpoint', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)) } }, { type: 'climate', name: 'running_state', property: 'running_state', deviceType: DeviceTypes.THERMOSTAT, cluster: Thermostat.Cluster.id, attribute: 'thermostatRunningMode', valueLookup: ['idle', '', '', 'cool', 'heat'] }, { type: 'climate', name: 'system_mode', property: 'system_mode', deviceType: DeviceTypes.THERMOSTAT, cluster: Thermostat.Cluster.id, attribute: 'systemMode', valueLookup: ['off', 'auto', '', 'cool', 'heat'] }, @@ -1057,13 +1059,23 @@ export class ZigbeeDevice extends ZigbeeEntity { this.log.debug(`Command setpointRaiseLower called for ${this.ien}${device.friendly_name}${rs}${db}`, request); if (request.mode === Thermostat.SetpointRaiseLowerMode.Heat && attributes.occupiedHeatingSetpoint) { const setpoint = Math.round(attributes.occupiedHeatingSetpoint.getLocal() / 100 + request.amount / 10); - this.publishCommand('OccupiedHeatingSetpoint', device.friendly_name, { current_heating_setpoint: setpoint }); - this.log.debug('Command setpointRaiseLower sent:', debugStringify({ current_heating_setpoint: setpoint })); + if (this.propertyMap.has('current_heating_setpoint')) { + this.publishCommand('OccupiedHeatingSetpoint', device.friendly_name, { current_heating_setpoint: setpoint }); + this.log.debug('Command setpointRaiseLower sent:', debugStringify({ current_heating_setpoint: setpoint })); + } else if (this.propertyMap.has('occupied_heating_setpoint')) { + this.publishCommand('OccupiedHeatingSetpoint', device.friendly_name, { occupied_heating_setpoint: setpoint }); + this.log.debug('Command setpointRaiseLower sent:', debugStringify({ occupied_heating_setpoint: setpoint })); + } } if (request.mode === Thermostat.SetpointRaiseLowerMode.Cool && attributes.occupiedCoolingSetpoint) { const setpoint = Math.round(attributes.occupiedCoolingSetpoint.getLocal() / 100 + request.amount / 10); - this.publishCommand('OccupiedCoolingSetpoint', device.friendly_name, { current_cooling_setpoint: setpoint }); - this.log.debug('Command setpointRaiseLower sent:', debugStringify({ current_cooling_setpoint: setpoint })); + if (this.propertyMap.has('current_cooling_setpoint')) { + this.publishCommand('OccupiedCoolingSetpoint', device.friendly_name, { current_cooling_setpoint: setpoint }); + this.log.debug('Command setpointRaiseLower sent:', debugStringify({ current_cooling_setpoint: setpoint })); + } else if (this.propertyMap.has('occupied_cooling_setpoint')) { + this.publishCommand('OccupiedCoolingSetpoint', device.friendly_name, { occupied_cooling_setpoint: setpoint }); + this.log.debug('Command setpointRaiseLower sent:', debugStringify({ occupied_cooling_setpoint: setpoint })); + } } }); const thermostat = this.bridgedDevice.getClusterServer(ThermostatCluster.with(Thermostat.Feature.Heating, Thermostat.Feature.Cooling, Thermostat.Feature.AutoMode)); @@ -1079,7 +1091,8 @@ export class ZigbeeDevice extends ZigbeeEntity { }); thermostat.subscribeOccupiedHeatingSetpointAttribute(async (value) => { this.log.debug(`Subscribe occupiedHeatingSetpoint called for ${this.ien}${device.friendly_name}${rs}${db} with:`, value); - this.publishCommand('OccupiedHeatingSetpoint', device.friendly_name, { current_heating_setpoint: Math.round(value / 100) }); + if (this.propertyMap.has('current_heating_setpoint')) this.publishCommand('OccupiedHeatingSetpoint', device.friendly_name, { current_heating_setpoint: Math.round(value / 100) }); + else if (this.propertyMap.has('occupied_heating_setpoint')) this.publishCommand('OccupiedHeatingSetpoint', device.friendly_name, { occupied_heating_setpoint: Math.round(value / 100) }); if (this.bridgedDevice) this.bridgedDevice.noUpdate = true; setTimeout(() => { if (this.bridgedDevice) this.bridgedDevice.noUpdate = false; @@ -1087,7 +1100,8 @@ export class ZigbeeDevice extends ZigbeeEntity { }); thermostat.subscribeOccupiedCoolingSetpointAttribute(async (value) => { this.log.debug(`Subscribe occupiedCoolingSetpoint called for ${this.ien}${device.friendly_name}${rs}${db} with:`, value); - this.publishCommand('OccupiedCoolingSetpoint', device.friendly_name, { current_cooling_setpoint: Math.round(value / 100) }); + if (this.propertyMap.has('current_cooling_setpoint')) this.publishCommand('OccupiedCoolingSetpoint', device.friendly_name, { current_cooling_setpoint: Math.round(value / 100) }); + else if (this.propertyMap.has('occupied_cooling_setpoint')) this.publishCommand('OccupiedCoolingSetpoint', device.friendly_name, { occupied_cooling_setpoint: Math.round(value / 100) }); if (this.bridgedDevice) this.bridgedDevice.noUpdate = true; setTimeout(() => { if (this.bridgedDevice) this.bridgedDevice.noUpdate = false; From 357a95db18054506fcd1978134e07af8e95fa71a Mon Sep 17 00:00:00 2001 From: Luligu Date: Fri, 22 Nov 2024 14:49:21 +0100 Subject: [PATCH 04/17] Add debug to childDevice --- src/entity.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/entity.ts b/src/entity.ts index 8390753..41413cb 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -770,7 +770,7 @@ export class ZigbeeDevice extends ZigbeeEntity { else this.bridgedDevice.addDeviceTypeWithClusterServer([z2m.deviceType], [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)]); } else { if (!this.bridgedDevice) this.bridgedDevice = new BridgedBaseDevice(this, [bridgedNode]); - /* const child = */ this.bridgedDevice.addChildDeviceTypeWithClusterServer(endpoint, [z2m.deviceType], [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)]); + /* const child = */ this.bridgedDevice.addChildDeviceTypeWithClusterServer(endpoint, [z2m.deviceType], [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)], undefined, this.log.logLevel === LogLevel.DEBUG); // if (endpoint === 'l1') addTagList(child, 0x07, 1, 'endpoint ' + endpoint); // if (endpoint === 'l2') addTagList(child, 0x07, 2, 'endpoint ' + endpoint); // if (endpoint === 'l1') addTagList(child, null, 0x43, 0x08, 'endpoint ' + endpoint); @@ -799,7 +799,7 @@ export class ZigbeeDevice extends ZigbeeEntity { this.propertyMap.set('action_' + actionsMap[a], { name, type: '', endpoint: 'switch_' + count, action: triggerMap[a] }); this.log.info(`-- Button ${count}: ${hk}${switchMap[a]}${nf} <=> ${zb}${actionsMap[a]}${nf}`); } - this.bridgedDevice.addChildDeviceTypeWithClusterServer('switch_' + count, [DeviceTypes.GENERIC_SWITCH], [...DeviceTypes.GENERIC_SWITCH.requiredServerClusters]); + this.bridgedDevice.addChildDeviceTypeWithClusterServer('switch_' + count, [DeviceTypes.GENERIC_SWITCH], [...DeviceTypes.GENERIC_SWITCH.requiredServerClusters], undefined, this.log.logLevel === LogLevel.DEBUG); } else { for (let i = 0; i < this.actions.length; i += 3) { const actionsMap: string[] = []; @@ -808,7 +808,7 @@ export class ZigbeeDevice extends ZigbeeEntity { this.propertyMap.set('action_' + actionsMap[a - i], { name, type: '', endpoint: 'switch_' + count, action: triggerMap[a - i] }); this.log.info(`-- Button ${count}: ${hk}${switchMap[a - i]}${nf} <=> ${zb}${actionsMap[a - i]}${nf}`); } - this.bridgedDevice.addChildDeviceTypeWithClusterServer('switch_' + count, [DeviceTypes.GENERIC_SWITCH], [...DeviceTypes.GENERIC_SWITCH.requiredServerClusters]); + this.bridgedDevice.addChildDeviceTypeWithClusterServer('switch_' + count, [DeviceTypes.GENERIC_SWITCH], [...DeviceTypes.GENERIC_SWITCH.requiredServerClusters], undefined, this.log.logLevel === LogLevel.DEBUG); count++; } } From 63bcc67a111868e56eacd7ddf9ec97b7d7f39472 Mon Sep 17 00:00:00 2001 From: Luligu Date: Fri, 22 Nov 2024 15:06:22 +0100 Subject: [PATCH 05/17] Added min_temperature_limit and max_temperature_limit converter --- CHANGELOG.md | 5 +++++ src/entity.ts | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e0e028..950404b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ All notable changes to this project will be documented in this file. ## [2.2.2] - 2024-10-22 +### Added + +- [thermostat]: Added min_temperature_limit and max_temperature_limit converter. +- [thermostat]: Added min_heat_setpoint_limit and max_heat_setpoint_limit converter. + ### Changed - [readme]: Updated install script. diff --git a/src/entity.ts b/src/entity.ts index 41413cb..1b1ae37 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -631,6 +631,10 @@ export const z2ms: ZigbeeToMatter[] = [ { type: 'climate', name: 'occupied_cooling_setpoint', property: 'occupied_cooling_setpoint', deviceType: DeviceTypes.THERMOSTAT, cluster: Thermostat.Cluster.id, attribute: 'occupiedCoolingSetpoint', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)) } }, { type: 'climate', name: 'running_state', property: 'running_state', deviceType: DeviceTypes.THERMOSTAT, cluster: Thermostat.Cluster.id, attribute: 'thermostatRunningMode', valueLookup: ['idle', '', '', 'cool', 'heat'] }, { type: 'climate', name: 'system_mode', property: 'system_mode', deviceType: DeviceTypes.THERMOSTAT, cluster: Thermostat.Cluster.id, attribute: 'systemMode', valueLookup: ['off', 'auto', '', 'cool', 'heat'] }, + { type: '', name: 'min_temperature_limit', property: 'min_temperature_limit', deviceType: DeviceTypes.THERMOSTAT, cluster: Thermostat.Cluster.id, attribute: 'minHeatSetpointLimit', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)) } }, + { type: '', name: 'max_temperature_limit', property: 'max_temperature_limit', deviceType: DeviceTypes.THERMOSTAT, cluster: Thermostat.Cluster.id, attribute: 'maxHeatSetpointLimit', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)) } }, + { type: '', name: 'min_heat_setpoint_limit', property: 'min_heat_setpoint_limit', deviceType: DeviceTypes.THERMOSTAT, cluster: Thermostat.Cluster.id, attribute: 'minHeatSetpointLimit', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)) } }, + { type: '', name: 'max_heat_setpoint_limit', property: 'max_heat_setpoint_limit', deviceType: DeviceTypes.THERMOSTAT, cluster: Thermostat.Cluster.id, attribute: 'maxHeatSetpointLimit', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)) } }, { type: '', name: 'presence', property: 'presence', deviceType: DeviceTypes.OCCUPANCY_SENSOR, cluster: OccupancySensing.Cluster.id, attribute: 'occupancy', converter: (value) => { return { occupied: value as boolean } } }, { type: '', name: 'occupancy', property: 'occupancy', deviceType: DeviceTypes.OCCUPANCY_SENSOR, cluster: OccupancySensing.Cluster.id, attribute: 'occupancy', converter: (value) => { return { occupied: value as boolean } } }, From dcc818825bc3e0fffcee2a5d7b4827b3f74c1438 Mon Sep 17 00:00:00 2001 From: Luligu Date: Fri, 22 Nov 2024 15:14:36 +0100 Subject: [PATCH 06/17] Restyle update attribute --- src/entity.ts | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/entity.ts b/src/entity.ts index 1b1ae37..62197b2 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -73,7 +73,7 @@ import { ClusterServer, VendorId, } from 'matterbridge'; -import { AnsiLogger, TimestampFormat, gn, dn, ign, idn, rs, db, wr, debugStringify, hk, zb, or, nf, LogLevel, CYAN } from 'matterbridge/logger'; +import { AnsiLogger, TimestampFormat, gn, dn, ign, idn, rs, db, wr, debugStringify, hk, zb, or, nf, LogLevel, CYAN, er } from 'matterbridge/logger'; import { deepCopy, deepEqual } from 'matterbridge/utils'; import * as color from 'matterbridge/utils'; @@ -264,17 +264,21 @@ export class ZigbeeEntity extends EventEmitter { }); } - protected updateAttributeIfChanged(rootEndpoint: Endpoint, endpointName: string | undefined, clusterId: number, attributeName: string, value: PayloadValue, lookup?: string[]): void { - if (endpointName && endpointName !== '') { - rootEndpoint = this.bridgedDevice?.getChildEndpointByName(endpointName) ?? rootEndpoint; + protected updateAttributeIfChanged(deviceEndpoint: Endpoint, childEndpointName: string | undefined, clusterId: number, attributeName: string, value: PayloadValue, lookup?: string[]): void { + if (childEndpointName && childEndpointName !== '') { + deviceEndpoint = this.bridgedDevice?.getChildEndpointByName(childEndpointName) ?? deviceEndpoint; } - const cluster = rootEndpoint.getClusterServerById(ClusterId(clusterId)); + const cluster = deviceEndpoint.getClusterServerById(ClusterId(clusterId)); if (cluster === undefined) { - this.log.debug(`*Update endpoint ${this.eidn}${rootEndpoint.name}:${rootEndpoint.number}${db}${endpointName ? ' (' + zb + endpointName + db + ')' : ''} cluster ${hk}${clusterId}${db}-${hk}${getClusterNameById(ClusterId(clusterId))}${db} not found: is z2m converter exposing all features?`); + this.log.debug( + `*Update endpoint ${this.eidn}${deviceEndpoint.name}:${deviceEndpoint.number}${db}${childEndpointName ? ' (' + zb + childEndpointName + db + ')' : ''} cluster ${hk}${clusterId}${db}-${hk}${getClusterNameById(ClusterId(clusterId))}${db} not found: is z2m converter exposing all features?`, + ); return; } if (!cluster.isAttributeSupportedByName(attributeName)) { - this.log.debug(`***Update endpoint ${this.eidn}${rootEndpoint.name}:${rootEndpoint.number}${db}${endpointName ? ' (' + zb + endpointName + db + ')' : ''} error attribute ${hk}${clusterId}${db}-${hk}${getClusterNameById(ClusterId(clusterId))}${db}-${hk}${attributeName}${db} not found`); + this.log.debug( + `***Update endpoint ${this.eidn}${deviceEndpoint.name}:${deviceEndpoint.number}${db}${childEndpointName ? ' (' + zb + childEndpointName + db + ')' : ''} error attribute ${hk}${clusterId}${db}-${hk}${getClusterNameById(ClusterId(clusterId))}${db}.${hk}${attributeName}${db} not found`, + ); return; } if (lookup !== undefined) { @@ -282,8 +286,8 @@ export class ZigbeeEntity extends EventEmitter { value = lookup.indexOf(value); } else { this.log.debug( - `***Update endpoint ${this.eidn}${rootEndpoint.name}:${rootEndpoint.number}${db}${endpointName ? ' (' + zb + endpointName + db + ')' : ''} ` + - `attribute ${hk}${getClusterNameById(ClusterId(clusterId))}${db}-${hk}${attributeName}${db} value ${zb}${typeof value === 'object' ? debugStringify(value) : value}${db} not found in lookup ${debugStringify(lookup)}`, + `***Update endpoint ${this.eidn}${deviceEndpoint.name}:${deviceEndpoint.number}${db}${childEndpointName ? ' (' + zb + childEndpointName + db + ')' : ''} ` + + `attribute ${hk}${getClusterNameById(ClusterId(clusterId))}${db}.${hk}${attributeName}${db} value ${zb}${typeof value === 'object' ? debugStringify(value) : value}${db} not found in lookup ${debugStringify(lookup)}`, ); return; } @@ -291,19 +295,19 @@ export class ZigbeeEntity extends EventEmitter { const localValue = cluster.attributes[attributeName].getLocal(); if (typeof value === 'object' ? deepEqual(value, localValue) : value === localValue) { this.log.debug( - `*Skip update endpoint ${this.eidn}${rootEndpoint.number}${db}${endpointName ? ' (' + zb + endpointName + db + ')' : ''} ` + - `attribute ${hk}${getClusterNameById(ClusterId(clusterId))}${db}-${hk}${attributeName}${db} already ${zb}${typeof value === 'object' ? debugStringify(value) : value}${db}`, + `*Skip update endpoint ${this.eidn}${deviceEndpoint.number}${db}${childEndpointName ? ' (' + zb + childEndpointName + db + ')' : ''} ` + + `attribute ${hk}${getClusterNameById(ClusterId(clusterId))}${db}.${hk}${attributeName}${db} already ${zb}${typeof value === 'object' ? debugStringify(value) : value}${db}`, ); return; } this.log.info( - `${db}Update endpoint ${this.eidn}${rootEndpoint.name}:${rootEndpoint.number}${db}${endpointName ? ' (' + zb + endpointName + db + ')' : ''} ` + - `attribute ${hk}${getClusterNameById(ClusterId(clusterId))}${db}-${hk}${attributeName}${db} from ${zb}${typeof localValue === 'object' ? debugStringify(localValue) : localValue}${db} to ${zb}${typeof value === 'object' ? debugStringify(value) : value}${db}`, + `${db}Update endpoint ${this.eidn}${deviceEndpoint.name}:${deviceEndpoint.number}${db}${childEndpointName ? ' (' + zb + childEndpointName + db + ')' : ''} ` + + `attribute ${hk}${getClusterNameById(ClusterId(clusterId))}${db}.${hk}${attributeName}${db} from ${zb}${typeof localValue === 'object' ? debugStringify(localValue) : localValue}${db} to ${zb}${typeof value === 'object' ? debugStringify(value) : value}${db}`, ); try { cluster.attributes[attributeName].setLocal(value); } catch (error) { - this.log.error(`Error setting attribute ${attributeName} to ${value}: ${error}`); + this.log.error(`Error setting attribute ${hk}${getClusterNameById(ClusterId(clusterId))}${er}.${hk}${attributeName}${er} to ${value}: ${error}`); } } From 682c2cb8217e9540c45bba28b0b2e8e494b4f475 Mon Sep 17 00:00:00 2001 From: Luligu Date: Fri, 22 Nov 2024 17:22:16 +0100 Subject: [PATCH 07/17] Pass username and password like undefined to mqtt --- CHANGELOG.md | 3 ++- matterbridge-zigbee2mqtt.schema.json | 11 ++++++++--- src/index.test.ts | 2 +- src/platform.test.ts | 2 +- src/platform.ts | 11 ++++++----- src/zigbee2mqtt.ts | 16 ++++++++-------- 6 files changed, 26 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 950404b..7a86bff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,10 +13,11 @@ All notable changes to this project will be documented in this file. ### Changed +- [mqtt]: Username and password are passed like undefined unless set. - [readme]: Updated install script. - [readme]: Updated build script. - [package]: Updated dependencies. -- [plugin]: Requires Matterbridge 1.6.0. +- [plugin]: Requires Matterbridge 1.6.1. ### Fixed diff --git a/matterbridge-zigbee2mqtt.schema.json b/matterbridge-zigbee2mqtt.schema.json index 4322620..234c143 100644 --- a/matterbridge-zigbee2mqtt.schema.json +++ b/matterbridge-zigbee2mqtt.schema.json @@ -2,7 +2,11 @@ "title": "Matterbridge zigbee2mqtt plugin", "description": "matterbridge-zigbee2mqtt v. 2.0.17 by https://github.com/Luligu", "type": "object", - "required": ["host", "port", "topic"], + "required": [ + "host", + "port", + "topic" + ], "properties": { "name": { "description": "Plugin name", @@ -39,7 +43,8 @@ }, "topic": { "description": "MQTT base topic for Zigbee2MQTT MQTT messages", - "type": "string" + "type": "string", + "default": "zigbee2mqtt" }, "blackList": { "description": "The devices in the list will not be exposed.", @@ -109,4 +114,4 @@ "default": false } } -} +} \ No newline at end of file diff --git a/src/index.test.ts b/src/index.test.ts index f165f9e..1e4a1d8 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -15,7 +15,7 @@ describe('initializePlugin', () => { matterbridgeDirectory: '', matterbridgePluginDirectory: 'temp', systemInformation: { ipv4Address: undefined }, - matterbridgeVersion: '1.6.0', + matterbridgeVersion: '1.6.1', removeAllBridgedDevices: jest.fn(), } as unknown as Matterbridge; mockLog = { error: jest.fn(), warn: jest.fn(), info: jest.fn(), debug: jest.fn() } as unknown as AnsiLogger; diff --git a/src/platform.test.ts b/src/platform.test.ts index 25bc159..d1bb3e8 100644 --- a/src/platform.test.ts +++ b/src/platform.test.ts @@ -18,7 +18,7 @@ describe('TestPlatform', () => { matterbridgeDirectory: '', matterbridgePluginDirectory: 'temp', systemInformation: { ipv4Address: undefined }, - matterbridgeVersion: '1.6.0', + matterbridgeVersion: '1.6.1', removeAllBridgedDevices: jest.fn(), } as unknown as Matterbridge; mockLog = { error: jest.fn(), warn: jest.fn(), info: jest.fn(), debug: jest.fn() } as unknown as AnsiLogger; diff --git a/src/platform.ts b/src/platform.ts index d05fe92..542e83f 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -48,8 +48,8 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { private mqttHost = 'localhost'; private mqttPort = 1883; private mqttTopic = 'zigbee2mqtt'; - private mqttUsername = ''; - private mqttPassword = ''; + private mqttUsername: string | undefined = undefined; + private mqttPassword: string | undefined = undefined; private mqttProtocol: 4 | 5 | 3 = 5; private whiteList: string[] = []; private blackList: string[] = []; @@ -78,10 +78,11 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { super(matterbridge, log, config); // Verify that Matterbridge is the correct version - if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('1.6.0')) { - throw new Error(`This plugin requires Matterbridge version >= "1.6.0". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend."`); + if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('1.6.1')) { + throw new Error(`This plugin requires Matterbridge version >= "1.6.1". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend."`); } + this.log.debug(`Config:')}${rs}`, config); this.debugEnabled = config.debug as boolean; this.shouldStart = false; this.shouldConfigure = false; @@ -121,7 +122,7 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { this.log.info(`Initializing platform: ${this.config.name}${rs} v${CYAN}${this.version}`); this.log.info(`Loaded zigbee2mqtt parameters from ${path.join(matterbridge.matterbridgeDirectory, 'matterbridge-zigbee2mqtt.config.json')}${rs}:`); - // this.log.debug(`Config:')}${rs}`, config); + this.log.debug(`Config:')}${rs}`, config); this.z2m = new Zigbee2MQTT(this.mqttHost, this.mqttPort, this.mqttTopic, this.mqttUsername, this.mqttPassword, this.mqttProtocol, this.debugEnabled); this.z2m.setLogDebug(this.debugEnabled); diff --git a/src/zigbee2mqtt.ts b/src/zigbee2mqtt.ts index a4b06c9..f250251 100644 --- a/src/zigbee2mqtt.ts +++ b/src/zigbee2mqtt.ts @@ -266,13 +266,13 @@ export class Zigbee2MQTT extends EventEmitter { protocolVersion: 5, reconnectPeriod: 5000, // 1000 connectTimeout: 60 * 1000, // 30 * 1000 - username: '', - password: '', + username: undefined, + password: undefined, clean: true, }; // Constructor - constructor(mqttHost: string, mqttPort: number, mqttTopic: string, mqttUsername = '', mqttPassword = '', protocolVersion: 4 | 5 | 3 = 5, debug = false) { + constructor(mqttHost: string, mqttPort: number, mqttTopic: string, mqttUsername: string | undefined = undefined, mqttPassword: string | undefined = undefined, protocolVersion: 4 | 5 | 3 = 5, debug = false) { super(); this.mqttHost = mqttHost; @@ -281,10 +281,8 @@ export class Zigbee2MQTT extends EventEmitter { this.mqttUsername = mqttUsername; this.mqttPassword = mqttPassword; - if (mqttUsername !== '' && mqttPassword !== '') { - this.options.username = mqttUsername; - this.options.password = mqttPassword; - } + this.options.username = mqttUsername !== undefined && mqttUsername !== '' ? mqttUsername : undefined; + this.options.password = mqttPassword !== undefined && mqttPassword !== '' ? mqttPassword : undefined; this.options.protocolVersion = protocolVersion; this.z2mIsAvailabilityEnabled = false; @@ -296,7 +294,9 @@ export class Zigbee2MQTT extends EventEmitter { this.z2mGroups = []; this.log = new AnsiLogger({ logName: 'Zigbee2MQTT', logTimestampFormat: TimestampFormat.TIME_MILLIS, logLevel: debug ? LogLevel.DEBUG : LogLevel.INFO }); - this.log.debug(`Created new instance with host: ${mqttHost} port: ${mqttPort} protocol ${protocolVersion} topic: ${mqttTopic} username: ${mqttUsername} password: ${mqttPassword !== '' ? '*****' : ''}`); + this.log.debug( + `Created new instance with host: ${mqttHost} port: ${mqttPort} protocol ${protocolVersion} topic: ${mqttTopic} username: ${mqttUsername !== undefined && mqttUsername !== '' ? mqttUsername : 'undefined'} password: ${mqttPassword !== undefined && mqttPassword !== '' ? '*****' : 'undefined'}`, + ); } public setLogDebug(logDebug: boolean): void { From dc79928cedb5d0359ad52958b980a4c0fb2dbcbd Mon Sep 17 00:00:00 2001 From: Luligu Date: Fri, 22 Nov 2024 17:55:37 +0100 Subject: [PATCH 08/17] Fix illuminance for USM-300ZB --- src/entity.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/entity.ts b/src/entity.ts index 62197b2..9169313 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -173,6 +173,9 @@ export class ZigbeeEntity extends EventEmitter { if (key === 'illuminance' && this.isDevice && this.device?.definition?.model === 'RTCGQ14LM') { key = 'illuminance_lux'; } + if (key === 'illuminance' && this.isDevice && this.device?.definition?.model === 'USM-300ZB') { + key = 'illuminance_lux'; + } if (key === 'illuminance' && !('illuminance_lux' in payload)) { key = 'illuminance_lux'; } From 6a6120e402281f3435eb79fc3d07c18babb6ac42 Mon Sep 17 00:00:00 2001 From: Luligu Date: Sat, 23 Nov 2024 19:37:51 +0100 Subject: [PATCH 09/17] Dev 2.2.2-dev.5 --- .gitignore | 1 + .npmignore | 1 + CHANGELOG.md | 3 +- create-release.js | 81 ---- eslint.config.js | 17 +- link-matterbridge-script.js | 1 - matterbridge-zigbee2mqtt.schema.json | 8 +- package-lock.json | 20 +- package.json | 10 +- src/entity.ts | 275 ++++++----- src/index.test.ts | 45 +- src/mock/bridge-payloads.txt | 702 +++++++++++++++++++++++++++ src/platform.test.ts | 264 ++++++++-- src/platform.ts | 95 ++-- src/zigbee2mqtt.ts | 33 +- tsconfig.eslint.json | 27 -- 16 files changed, 1208 insertions(+), 375 deletions(-) delete mode 100644 create-release.js delete mode 100644 link-matterbridge-script.js create mode 100644 src/mock/bridge-payloads.txt delete mode 100644 tsconfig.eslint.json diff --git a/.gitignore b/.gitignore index 6024f0b..5739bac 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ hs_err_pid* replay_pid* # zigbee2mqtt +temp/ bridge-info.json bridge-devices.json bridge-groups.json diff --git a/.npmignore b/.npmignore index 3abbd73..485b4eb 100644 --- a/.npmignore +++ b/.npmignore @@ -162,5 +162,6 @@ jest.config.js screenshot # other stuff +temp/ TODO.md yellow-button.png \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a86bff..1c94206 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,13 @@ If you like this project and find it useful, please consider giving it a star on All notable changes to this project will be documented in this file. -## [2.2.2] - 2024-10-22 +## [2.2.2] - 2024-11-22 ### Added - [thermostat]: Added min_temperature_limit and max_temperature_limit converter. - [thermostat]: Added min_heat_setpoint_limit and max_heat_setpoint_limit converter. +- [matter]: Added tagList to child endpoints. ### Changed diff --git a/create-release.js b/create-release.js deleted file mode 100644 index acfc1b8..0000000 --- a/create-release.js +++ /dev/null @@ -1,81 +0,0 @@ -/* eslint-disable no-console */ - -/* -Add the following scripts to package.json file: - "prepublishOnly": "npm run lint && npm run test && npm run cleanBuild", - "npmPublish": "npm publish", - "gitPublish": "npm run lint && npm run test && npm run cleanBuild && node create-release.js", - "preversion": "npm run lint && npm run test && npm run cleanBuild", - "postversion": "git push && git push --tags && node create-release.js", - "version:patch": "npm version patch", - "version:minor": "npm version minor", - "version:major": "npm version major", -*/ - -import { execSync } from 'child_process'; -import { readFileSync, writeFileSync, unlinkSync } from 'fs'; -import path from 'path'; -import readline from 'readline'; - -// Get the latest tag -let tag = execSync('git describe --tags --abbrev=0').toString().trim(); -if (tag.startsWith('v')) { - tag = tag.substring(1); -} - -// Read the changelog file -const changelogPath = path.join(process.cwd(), 'CHANGELOG.md'); -const changelog = readFileSync(changelogPath, 'utf8'); - -// Extract the relevant section from the changelog -const changelogSection = extractChangelogSection(changelog, tag); - -const title = `Release ${tag}`; -const notes = `Release notes for version ${tag}\n\n## [${tag}] ${changelogSection}`; - -// Log the release details -console.log(`Creating release ${tag} with the following details:\nTitle:\n${title}\nNotes:\n${notes}`); - -// Write the release notes to a temporary file -const notesFilePath = path.join(process.cwd(), 'release-notes.md'); -writeFileSync(notesFilePath, notes); - -// Wait for user input before proceeding -await pressAnyKey(); - -// Create the release using the temporary file -execSync(`gh release create ${tag} -t "${title}" -F "${notesFilePath}"`, { stdio: 'inherit' }); - -// Clean up the temporary file -unlinkSync(notesFilePath); - -/** - * Extracts the relevant section from the changelog for the given tag. - * Assumes that each version section in the changelog starts with a heading like "## [tag]". - * @param {string} changelog - The content of the changelog file. - * @param {string} tag - The tag for which to extract the changelog section. - * @returns {string} - The extracted changelog section. - */ -function extractChangelogSection(changelog, tag) { - const regex = new RegExp(`## \\[${tag}\\](.*?)(## \\[|$)`, 's'); - const match = changelog.match(regex); - return match ? match[1].trim() : 'No changelog entry found for this version.'; -} - -/** - * Waits for the user to press any key. - * @returns {Promise} - */ -function pressAnyKey() { - return new Promise((resolve) => { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); - - rl.question('Press any key to continue...', () => { - rl.close(); - resolve(); - }); - }); -} diff --git a/eslint.config.js b/eslint.config.js index f2e03c1..610a265 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -8,7 +8,7 @@ import eslintPluginPrettier from 'eslint-plugin-prettier/recommended'; export default [ { name: 'global ignores', - ignores: ['dist/', 'build/', 'node_modules/', 'coverage/', 'frontend/'], + ignores: ['**/dist/', '**/build/', '**/node_modules/', '**/coverage/', '**/frontend/', '**/rock-s0/'], }, eslint.configs.recommended, ...tseslint.configs.strict, @@ -20,13 +20,6 @@ export default [ languageOptions: { ecmaVersion: 'latest', sourceType: 'module', - /* - parser: tseslint.parser, - parserOptions: { - project: './tsconfig.eslint.json', - tsconfigRootDir: import.meta.dirname, - }, - */ }, linterOptions: { reportUnusedDisableDirectives: 'warn', @@ -41,7 +34,7 @@ export default [ { name: 'javascript', files: ['**/*.js'], - // ...tseslint.configs.disableTypeChecked, + ...tseslint.configs.disableTypeChecked, }, { name: 'typescript', @@ -66,13 +59,11 @@ export default [ { name: 'jest', files: ['**/__test__/*', '**/*.test.ts', '**/*.spec.ts'], - // ...tseslint.configs.disableTypeChecked, plugins: { '@typescript-eslint': tseslint.plugin, jest: jesteslint, }, - rules: { - ...jesteslint.configs['flat/recommended'].rules, - }, + ...tseslint.configs.disableTypeChecked, + ...jesteslint.configs['flat/recommended'], }, ]; diff --git a/link-matterbridge-script.js b/link-matterbridge-script.js deleted file mode 100644 index 1c61e10..0000000 --- a/link-matterbridge-script.js +++ /dev/null @@ -1 +0,0 @@ -// Nothing to do in production, just a script to link the matterbridge package in development diff --git a/matterbridge-zigbee2mqtt.schema.json b/matterbridge-zigbee2mqtt.schema.json index 234c143..04e0e01 100644 --- a/matterbridge-zigbee2mqtt.schema.json +++ b/matterbridge-zigbee2mqtt.schema.json @@ -2,11 +2,7 @@ "title": "Matterbridge zigbee2mqtt plugin", "description": "matterbridge-zigbee2mqtt v. 2.0.17 by https://github.com/Luligu", "type": "object", - "required": [ - "host", - "port", - "topic" - ], + "required": ["host", "port", "topic"], "properties": { "name": { "description": "Plugin name", @@ -114,4 +110,4 @@ "default": false } } -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index 290cc83..fac8437 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "matterbridge-zigbee2mqtt", - "version": "2.2.2-dev.1", + "version": "2.2.2-dev.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "matterbridge-zigbee2mqtt", - "version": "2.2.2-dev.1", + "version": "2.2.2-dev.5", "license": "Apache-2.0", "dependencies": { "moment": "2.30.1", @@ -18,7 +18,7 @@ "@eslint/js": "9.15.0", "@types/eslint__js": "8.42.3", "@types/jest": "29.5.14", - "@types/node": "22.9.1", + "@types/node": "22.9.3", "eslint": "9.15.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-jest": "28.9.0", @@ -26,7 +26,7 @@ "jest": "29.7.0", "prettier": "3.3.3", "ts-jest": "29.2.5", - "typescript": "5.6.3", + "typescript": "5.7.2", "typescript-eslint": "8.15.0" }, "engines": { @@ -1438,9 +1438,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.1.tgz", - "integrity": "sha512-p8Yy/8sw1caA8CdRIQBG5tiLHmxtQKObCijiAa9Ez+d4+PRffM4054xbju0msf+cvhJpnFEeNjxmVT/0ipktrg==", + "version": "22.9.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.3.tgz", + "integrity": "sha512-F3u1fs/fce3FFk+DAxbxc78DF8x0cY09RRL8GnXLmkJ1jvx3TtPdWoTT5/NiYfI5ASqXBmfqJi9dZ3gxMx4lzw==", "license": "MIT", "dependencies": { "undici-types": "~6.19.8" @@ -5417,9 +5417,9 @@ "license": "MIT" }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/package.json b/package.json index cb2a8af..e8e4e2f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matterbridge-zigbee2mqtt", - "version": "2.2.2-dev.1", + "version": "2.2.2-dev.5", "description": "Matterbridge zigbee2mqtt plugin", "author": "https://github.com/Luligu", "license": "Apache-2.0", @@ -48,9 +48,9 @@ "start:bridge": "matterbridge -bridge", "start:childbridge": "matterbridge -childbridge", "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js", - "test:verbose": "node --experimental-vm-modules node_modules/jest/bin/jest.js --verbose", + "test:verbose": "node --experimental-vm-modules node_modules/jest/bin/jest.js --detectOpenHandles --verbose", "test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch", - "test:coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage", + "test:coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js --detectOpenHandles --verbose --coverage", "lint": "eslint --max-warnings=0 .", "lint:fix": "eslint --fix --max-warnings=0 .", "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,md}\"", @@ -86,7 +86,7 @@ "@eslint/js": "9.15.0", "@types/eslint__js": "8.42.3", "@types/jest": "29.5.14", - "@types/node": "22.9.1", + "@types/node": "22.9.3", "eslint": "9.15.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-jest": "28.9.0", @@ -94,7 +94,7 @@ "jest": "29.7.0", "prettier": "3.3.3", "ts-jest": "29.2.5", - "typescript": "5.6.3", + "typescript": "5.7.2", "typescript-eslint": "8.15.0" } } \ No newline at end of file diff --git a/src/entity.ts b/src/entity.ts index 9169313..5cd07bc 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -4,9 +4,9 @@ * @file entity.ts * @author Luca Liguori * @date 2023-12-29 - * @version 3.0.1 + * @version 3.1.0 * - * Copyright 2023, 2024 Luca Liguori. + * Copyright 2023, 2024, 2025 Luca Liguori. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -72,8 +72,9 @@ import { Descriptor, ClusterServer, VendorId, + EndpointOptions, } from 'matterbridge'; -import { AnsiLogger, TimestampFormat, gn, dn, ign, idn, rs, db, wr, debugStringify, hk, zb, or, nf, LogLevel, CYAN, er } from 'matterbridge/logger'; +import { AnsiLogger, TimestampFormat, gn, dn, ign, idn, rs, db, debugStringify, hk, zb, or, nf, LogLevel, CYAN, er } from 'matterbridge/logger'; import { deepCopy, deepEqual } from 'matterbridge/utils'; import * as color from 'matterbridge/utils'; @@ -96,7 +97,8 @@ export class ZigbeeEntity extends EventEmitter { public actions: string[] = []; protected en = ''; protected ien = ''; - public bridgedDevice: BridgedBaseDevice | undefined; + // public bridgedDevice: BridgedBaseDevice | undefined; + public bridgedDevice: MatterbridgeDevice | undefined; public eidn = `${or}`; private lastPayload: Payload = {}; private lastSeen = 0; @@ -104,6 +106,89 @@ export class ZigbeeEntity extends EventEmitter { protected transition = false; protected propertyMap = new Map(); + colorTimeout: NodeJS.Timeout | undefined = undefined; + thermostatTimeout: NodeJS.Timeout | undefined = undefined; + + protected hasEndpoints = false; + public isRouter = false; + protected noUpdate = false; + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + createDevice(definition: DeviceTypeDefinition | AtLeastOne, includeServerList: ClusterId[] = [], options?: EndpointOptions, debug?: boolean) { + this.bridgedDevice = new MatterbridgeDevice(definition, undefined, this.log.logLevel === LogLevel.DEBUG); + this.bridgedDevice.addClusterServerFromList(this.bridgedDevice, includeServerList); + + // Add bridgedNode device type and BridgedDeviceBasicInformation cluster + this.bridgedDevice.addDeviceType(bridgedNode); + if (this.isDevice && this.device && this.device.friendly_name === 'Coordinator') { + this.bridgedDevice.createDefaultBridgedDeviceBasicInformationClusterServer(this.device.friendly_name, this.serial, 0xfff1, 'zigbee2MQTT', 'Coordinator'); + } else if (this.isDevice && this.device) { + this.bridgedDevice.createDefaultBridgedDeviceBasicInformationClusterServer(this.device.friendly_name, this.serial, 0xfff1, this.device.definition ? this.device.definition.vendor : this.device.manufacturer, this.device.definition ? this.device.definition.model : this.device.model_id); + } else if (this.isGroup && this.group) { + this.bridgedDevice.createDefaultBridgedDeviceBasicInformationClusterServer(this.group.friendly_name, this.serial, 0xfff1, 'zigbee2MQTT', 'Group'); + } + + // Add powerSource device type and PowerSource cluster + this.bridgedDevice.addDeviceType(powerSource); + if (this.isDevice) { + if (this.device?.power_source === 'Battery') this.bridgedDevice.createDefaultPowerSourceReplaceableBatteryClusterServer(100, PowerSource.BatChargeLevel.Ok); + else this.bridgedDevice.createDefaultPowerSourceWiredClusterServer(); + } else if (this.isGroup) { + this.bridgedDevice.createDefaultPowerSourceWiredClusterServer(); + } + return this.bridgedDevice; + } + + configure() { + if (this.bridgedDevice?.getClusterServerById(WindowCovering.Cluster.id)) { + this.log.info(`Configuring ${this.bridgedDevice?.deviceName} WindowCovering cluster`); + this.bridgedDevice?.setWindowCoveringTargetAsCurrentAndStopped(); + } + if (this.bridgedDevice?.getClusterServerById(DoorLock.Cluster.id)) { + this.log.info(`Configuring ${this.bridgedDevice?.deviceName} DoorLock cluster`); + const state = this.bridgedDevice?.getClusterServerById(DoorLock.Cluster.id)?.getLockStateAttribute(); + if (this.bridgedDevice.number) { + if (state === DoorLock.LockState.Locked) this.bridgedDevice?.getClusterServer(DoorLockCluster)?.triggerLockOperationEvent({ lockOperationType: DoorLock.LockOperationType.Lock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }); + if (state === DoorLock.LockState.Unlocked) this.bridgedDevice?.getClusterServer(DoorLockCluster)?.triggerLockOperationEvent({ lockOperationType: DoorLock.LockOperationType.Unlock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }); + } + } + } + + // Use with this.addTagList(endpoint, null, NumberTag.One.namespaceId, NumberTag.One.tag, 'Label'); + // this.addTagList(endpoint, null, SwitchesTag.Custom.namespaceId, SwitchesTag.Custom.tag, 'Label'); + addTagList(endpoint: Endpoint, mfgCode: VendorId | null, namespaceId: number, tag: number, label?: string | null) { + const descriptor = endpoint.getClusterServer(DescriptorCluster.with(Descriptor.Feature.TagList)); + if (!descriptor) { + this.log.error(`addTagList: descriptor cluster not found on endpoint ${endpoint.name}:${endpoint.number}`); + return; + } + + // tagList: { mfgCode: VendorId | null; namespaceId: number; tag: number; label?: string | null }[] = []; + + if (descriptor.attributes.tagList) { + this.log.debug(`addTagList: adding ${CYAN}tagList${db} mfCode: ${mfgCode}, namespaceId: ${namespaceId}, tag: ${tag}, label: ${label} on endpoint $${endpoint.name}:${endpoint.number}`); + const tagList = descriptor.attributes.tagList.getLocal(); + tagList.push({ mfgCode, namespaceId, tag, label }); + return; + } + + this.log.debug(`addTagList: creating ${CYAN}tagList${db} mfCode: ${mfgCode}, namespaceId: ${namespaceId}, tag: ${tag}, label: ${label} on endpoint $${endpoint.name}:${endpoint.number}`); + endpoint.addClusterServer( + ClusterServer( + DescriptorCluster.with(Descriptor.Feature.TagList), + { + tagList: [{ mfgCode, namespaceId, tag, label }], + deviceTypeList: [...descriptor.attributes.deviceTypeList.getLocal()], + serverList: [...descriptor.attributes.serverList.getLocal()], + clientList: [...descriptor.attributes.clientList.getLocal()], + partsList: [...descriptor.attributes.partsList.getLocal()], + }, + {}, + {}, + ), + ); + } + constructor(platform: ZigbeePlatform, entity: BridgeDevice | BridgeGroup) { super(); this.platform = platform; @@ -150,7 +235,7 @@ export class ZigbeeEntity extends EventEmitter { this.log.debug(`*Skipping (no device) ${platform.z2mDevicesRegistered ? 'MQTT message' : 'State update'} for accessory ${this.ien}${this.entityName}${rs}${db} payload: ${debugStringify(payload)}`); return; } - if (this.bridgedDevice.noUpdate) { + if (this.noUpdate) { this.log.debug(`*Skipping (no update) ${platform.z2mDevicesRegistered ? 'MQTT message' : 'State update'} for accessory ${this.ien}${this.entityName}${rs}${db} payload: ${debugStringify(payload)}`); return; } @@ -160,7 +245,7 @@ export class ZigbeeEntity extends EventEmitter { Object.entries(payload).forEach(([key, value]) => { // Skip null and undefined values if (value === undefined || value === null) return; - if (this.bridgedDevice === undefined || this.bridgedDevice.noUpdate) return; + if (this.bridgedDevice === undefined || this.noUpdate) return; // Modify voltage to battery_voltage if (key === 'voltage' && this.isDevice && this.device?.power_source === 'Battery') key = 'battery_voltage'; @@ -250,23 +335,30 @@ export class ZigbeeEntity extends EventEmitter { this.log.info(`ONLINE message for device ${this.ien}${this.entityName}${rs}`); if (this.bridgedDevice?.number !== undefined) { this.bridgedDevice?.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.setReachableAttribute(true); - this.bridgedDevice?.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: true }); + if (this.bridgedDevice.number) this.bridgedDevice?.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: true }); this.log.info(`${db}Set accessory attribute ${hk}BridgedDeviceBasicInformation.reachable: true`); this.log.info(`${db}Trigger accessory event ${hk}ReachableChangedEvent: true`); } }); this.platform.z2m.on('OFFLINE-' + this.entityName, () => { - this.log.warn(`OFFLINE message for device ${this.ien}${this.entityName}${wr}`); + this.log.warn(`OFFLINE message for device ${this.ien}${this.entityName}${rs}`); if (this.bridgedDevice?.number !== undefined) { this.bridgedDevice?.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.setReachableAttribute(false); - this.bridgedDevice?.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false }); + if (this.bridgedDevice.number) this.bridgedDevice?.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false }); this.log.info(`${db}Set accessory attribute ${hk}BridgedDeviceBasicInformation.reachable: false`); this.log.info(`${db}Trigger accessory event ${hk}ReachableChangedEvent: false`); } }); } + destroy() { + if (this.colorTimeout) clearTimeout(this.colorTimeout); + this.colorTimeout = undefined; + if (this.thermostatTimeout) clearTimeout(this.thermostatTimeout); + this.thermostatTimeout = undefined; + } + protected updateAttributeIfChanged(deviceEndpoint: Endpoint, childEndpointName: string | undefined, clusterId: number, attributeName: string, value: PayloadValue, lookup?: string[]): void { if (childEndpointName && childEndpointName !== '') { deviceEndpoint = this.bridgedDevice?.getChildEndpointByName(childEndpointName) ?? deviceEndpoint; @@ -347,7 +439,7 @@ export class ZigbeeGroup extends ZigbeeEntity { if (group.members.length === 0) { // Create a virtual device for the empty group to use in automations this.log.debug(`Group: ${gn}${group.friendly_name}${rs}${db} is a ${CYAN}virtual${db} group`); - this.bridgedDevice = new BridgedBaseDevice(this, [onOffSwitch], [...onOffSwitch.requiredServerClusters]); + this.bridgedDevice = this.createDevice([onOffSwitch], [...onOffSwitch.requiredServerClusters]); isSwitch = true; this.propertyMap.set('state', { name: 'state', type: 'switch', endpoint: '' }); } else { @@ -426,7 +518,7 @@ export class ZigbeeGroup extends ZigbeeEntity { this.propertyMap.set('system_mode', { name: 'system_mode', type: 'climate', endpoint: '' }); } if (!deviceType) return; - this.bridgedDevice = new BridgedBaseDevice(this, [deviceType], [...deviceType.requiredServerClusters]); + this.bridgedDevice = this.createDevice([deviceType], [...deviceType.requiredServerClusters]); } if (!this.bridgedDevice) return; @@ -478,13 +570,12 @@ export class ZigbeeGroup extends ZigbeeEntity { if (this.bridgedDevice.hasClusterServer(ColorControl.Complete) && this.bridgedDevice.getClusterServer(ColorControlCluster)?.isAttributeSupportedByName('currentHue')) { let lastRequestedHue = 0; let lastRequestedSaturation = 0; - let lastRequestTimeout: NodeJS.Timeout; this.bridgedDevice.addCommandHandler('moveToHue', async ({ request: request, attributes: attributes }) => { this.log.debug(`Command moveToHue called for ${this.ien}${group.friendly_name}${rs}${db} request: ${request.hue} attributes: hue ${attributes.currentHue?.getLocal()} saturation ${attributes.currentSaturation?.getLocal()} colorMode ${attributes.colorMode.getLocal()}`); attributes.colorMode.setLocal(ColorControl.ColorMode.CurrentHueAndCurrentSaturation); lastRequestedHue = request.hue; - lastRequestTimeout = setTimeout(() => { - clearTimeout(lastRequestTimeout); + this.colorTimeout = setTimeout(() => { + clearTimeout(this.colorTimeout); const rgb = color.hslColorToRgbColor((request.hue / 254) * 360, (lastRequestedSaturation / 254) * 100, 50); this.publishCommand('moveToHue', group.friendly_name, { color: { r: rgb.r, g: rgb.g, b: rgb.b } }); }, 500); @@ -493,8 +584,8 @@ export class ZigbeeGroup extends ZigbeeEntity { this.log.debug(`Command moveToSaturation called for ${this.ien}${group.friendly_name}${rs}${db} request: ${request.saturation} attributes: hue ${attributes.currentHue?.getLocal()} saturation ${attributes.currentSaturation?.getLocal()} colorMode ${attributes.colorMode.getLocal()}`); attributes.colorMode.setLocal(ColorControl.ColorMode.CurrentHueAndCurrentSaturation); lastRequestedSaturation = request.saturation; - lastRequestTimeout = setTimeout(() => { - clearTimeout(lastRequestTimeout); + this.colorTimeout = setTimeout(() => { + clearTimeout(this.colorTimeout); const rgb = color.hslColorToRgbColor((lastRequestedHue / 254) * 360, (request.saturation / 254) * 100, 50); this.publishCommand('moveToSaturation', group.friendly_name, { color: { r: rgb.r, g: rgb.g, b: rgb.b } }); }, 500); @@ -558,9 +649,9 @@ export class ZigbeeGroup extends ZigbeeEntity { } else if (newValue === Thermostat.SystemMode.Cool) { this.publishCommand('SystemMode', group.friendly_name, { system_mode: 'cool' }); } - if (this.bridgedDevice) this.bridgedDevice.noUpdate = true; - setTimeout(() => { - if (this.bridgedDevice) this.bridgedDevice.noUpdate = false; + this.noUpdate = true; + this.thermostatTimeout = setTimeout(() => { + this.noUpdate = false; }, 2 * 1000); } }, @@ -575,9 +666,9 @@ export class ZigbeeGroup extends ZigbeeEntity { this.bridgedDevice?.log.info(`Thermostat occupiedHeatingSetpoint changed from ${oldValue / 100} to ${newValue / 100}`); this.bridgedDevice?.log.info(`Setting thermostat occupiedHeatingSetpoint to ${newValue / 100}`); this.publishCommand('OccupiedHeatingSetpoint', group.friendly_name, { current_heating_setpoint: Math.round(newValue / 100) }); - if (this.bridgedDevice) this.bridgedDevice.noUpdate = true; - setTimeout(() => { - if (this.bridgedDevice) this.bridgedDevice.noUpdate = false; + this.noUpdate = true; + this.thermostatTimeout = setTimeout(() => { + this.noUpdate = false; }, 2 * 1000); }, this.bridgedDevice.log, @@ -591,9 +682,9 @@ export class ZigbeeGroup extends ZigbeeEntity { this.bridgedDevice?.log.info(`Thermostat occupiedCoolingSetpoint changed from ${oldValue / 100} to ${newValue / 100}`); this.bridgedDevice?.log.info(`Setting thermostat occupiedCoolingSetpoint to ${newValue / 100}`); this.publishCommand('OccupiedCoolingSetpoint', group.friendly_name, { current_cooling_setpoint: Math.round(newValue / 100) }); - if (this.bridgedDevice) this.bridgedDevice.noUpdate = true; - setTimeout(() => { - if (this.bridgedDevice) this.bridgedDevice.noUpdate = false; + this.noUpdate = true; + this.thermostatTimeout = setTimeout(() => { + this.noUpdate = false; }, 2 * 1000); }, this.bridgedDevice.log, @@ -685,9 +776,9 @@ export class ZigbeeDevice extends ZigbeeEntity { } if (device.friendly_name === 'Coordinator' || (device.model_id === 'ti.router' && device.manufacturer === 'TexasInstruments') || (device.model_id.startsWith('SLZB-') && device.manufacturer === 'SMLIGHT')) { - this.bridgedDevice = new BridgedBaseDevice(this, [DeviceTypes.DOOR_LOCK], [Identify.Cluster.id, DoorLock.Cluster.id]); + this.bridgedDevice = this.createDevice([DeviceTypes.DOOR_LOCK], [Identify.Cluster.id, DoorLock.Cluster.id]); this.bridgedDevice.addFixedLabel('type', 'lock'); - this.bridgedDevice.isRouter = true; + this.isRouter = true; } // Get types and properties @@ -777,17 +868,19 @@ export class ZigbeeDevice extends ZigbeeEntity { this.log.debug(`Device ${this.ien}${device.friendly_name}${rs}${db} endpoint: ${zb}${endpoint}${db} type: ${zb}${type}${db} property: ${zb}${name}${db} => deviceType: ${z2m.deviceType?.name} cluster: ${z2m.cluster} attribute: ${z2m.attribute}`); this.propertyMap.set(property, { name, type, endpoint, unit }); if (endpoint === '') { - if (!this.bridgedDevice) this.bridgedDevice = new BridgedBaseDevice(this, [z2m.deviceType], [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)]); + if (!this.bridgedDevice) this.bridgedDevice = this.createDevice([z2m.deviceType], [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)]); else this.bridgedDevice.addDeviceTypeWithClusterServer([z2m.deviceType], [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)]); } else { - if (!this.bridgedDevice) this.bridgedDevice = new BridgedBaseDevice(this, [bridgedNode]); - /* const child = */ this.bridgedDevice.addChildDeviceTypeWithClusterServer(endpoint, [z2m.deviceType], [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)], undefined, this.log.logLevel === LogLevel.DEBUG); - // if (endpoint === 'l1') addTagList(child, 0x07, 1, 'endpoint ' + endpoint); - // if (endpoint === 'l2') addTagList(child, 0x07, 2, 'endpoint ' + endpoint); - // if (endpoint === 'l1') addTagList(child, null, 0x43, 0x08, 'endpoint ' + endpoint); - // if (endpoint === 'l2') addTagList(child, null, 0x43, 0x08, 'endpoint ' + endpoint); + if (!this.bridgedDevice) this.bridgedDevice = this.createDevice([bridgedNode]); + /* const child = */ this.bridgedDevice.addChildDeviceTypeWithClusterServer(endpoint, [z2m.deviceType], [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)] /* , undefined, this.log.logLevel === LogLevel.DEBUG*/); + /* + if (endpoint === 'l1') this.addTagList(child, null, NumberTag.One.namespaceId, NumberTag.One.tag, 'endpoint ' + endpoint); + if (endpoint === 'l1') this.addTagList(child, null, SwitchesTag.Custom.namespaceId, SwitchesTag.Custom.tag, 'endpoint ' + endpoint); + if (endpoint === 'l2') this.addTagList(child, null, NumberTag.Two.namespaceId, NumberTag.Two.tag, 'endpoint ' + endpoint); + if (endpoint === 'l2') this.addTagList(child, null, SwitchesTag.Custom.namespaceId, SwitchesTag.Custom.tag, 'endpoint ' + endpoint); + */ this.bridgedDevice.addFixedLabel('composed', type); - this.bridgedDevice.hasEndpoints = true; + this.hasEndpoints = true; } } else { // this.log.debug(`Device ${this.ien}${device.friendly_name}${rs}${db} endpoint: ${zb}${endpoint}${db} type: ${zb}${type}${db} property: ${zb}${name}${db} => no mapping found`); @@ -797,8 +890,8 @@ export class ZigbeeDevice extends ZigbeeEntity { if (name === 'action' && this.actions.length) { this.log.info(`Device ${this.ien}${device.friendly_name}${rs}${nf} has actions mapped to these switches on sub endpoints:`); this.log.info(' controller events <=> zigbee2mqtt actions'); - if (!this.bridgedDevice) this.bridgedDevice = new BridgedBaseDevice(this, [bridgedNode]); - this.bridgedDevice.hasEndpoints = true; + if (!this.bridgedDevice) this.bridgedDevice = this.createDevice([bridgedNode]); + this.hasEndpoints = true; // Mapping actions const switchMap = ['Single Press', 'Double Press', 'Long Press ']; const triggerMap = ['Single', 'Double', 'Long']; @@ -810,7 +903,7 @@ export class ZigbeeDevice extends ZigbeeEntity { this.propertyMap.set('action_' + actionsMap[a], { name, type: '', endpoint: 'switch_' + count, action: triggerMap[a] }); this.log.info(`-- Button ${count}: ${hk}${switchMap[a]}${nf} <=> ${zb}${actionsMap[a]}${nf}`); } - this.bridgedDevice.addChildDeviceTypeWithClusterServer('switch_' + count, [DeviceTypes.GENERIC_SWITCH], [...DeviceTypes.GENERIC_SWITCH.requiredServerClusters], undefined, this.log.logLevel === LogLevel.DEBUG); + this.bridgedDevice.addChildDeviceTypeWithClusterServer('switch_' + count, [DeviceTypes.GENERIC_SWITCH], [...DeviceTypes.GENERIC_SWITCH.requiredServerClusters] /* , undefined, this.log.logLevel === LogLevel.DEBUG*/); } else { for (let i = 0; i < this.actions.length; i += 3) { const actionsMap: string[] = []; @@ -819,7 +912,7 @@ export class ZigbeeDevice extends ZigbeeEntity { this.propertyMap.set('action_' + actionsMap[a - i], { name, type: '', endpoint: 'switch_' + count, action: triggerMap[a - i] }); this.log.info(`-- Button ${count}: ${hk}${switchMap[a - i]}${nf} <=> ${zb}${actionsMap[a - i]}${nf}`); } - this.bridgedDevice.addChildDeviceTypeWithClusterServer('switch_' + count, [DeviceTypes.GENERIC_SWITCH], [...DeviceTypes.GENERIC_SWITCH.requiredServerClusters], undefined, this.log.logLevel === LogLevel.DEBUG); + this.bridgedDevice.addChildDeviceTypeWithClusterServer('switch_' + count, [DeviceTypes.GENERIC_SWITCH], [...DeviceTypes.GENERIC_SWITCH.requiredServerClusters] /* , undefined, this.log.logLevel === LogLevel.DEBUG*/); count++; } } @@ -921,7 +1014,7 @@ export class ZigbeeDevice extends ZigbeeEntity { this.log.debug(`Command identify called for ${this.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number} request identifyTime:${data.request.identifyTime} identifyTime:${data.attributes.identifyTime.getLocal()} identifyType:${data.attributes.identifyType.getLocal()} `); // logEndpoint(this.bridgedDevice!); }); - if (this.bridgedDevice.hasClusterServer(OnOff.Complete) || this.bridgedDevice.hasEndpoints) { + if (this.bridgedDevice.hasClusterServer(OnOff.Complete) || this.hasEndpoints) { this.bridgedDevice.addCommandHandler('on', async (data) => { if (!data.endpoint.number) return; this.log.debug(`Command on called for ${this.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number} onOff: ${data.attributes.onOff.getLocal()}`); @@ -950,7 +1043,7 @@ export class ZigbeeDevice extends ZigbeeEntity { this.publishCommand('toggle', device.friendly_name, payload); }); } - if (this.bridgedDevice.hasClusterServer(LevelControl.Complete) || this.bridgedDevice.hasEndpoints) { + if (this.bridgedDevice.hasClusterServer(LevelControl.Complete) || this.hasEndpoints) { this.bridgedDevice.addCommandHandler('moveToLevel', async (data) => { if (!data.endpoint.number) return; this.log.debug(`Command moveToLevel called for ${this.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number} request: ${data.request.level} transition: ${data.request.transitionTime} attributes: ${data.attributes.currentLevel.getLocal()}`); @@ -984,13 +1077,12 @@ export class ZigbeeDevice extends ZigbeeEntity { if (this.bridgedDevice.hasClusterServer(ColorControl.Complete) && this.bridgedDevice.getClusterServer(ColorControlCluster)?.isAttributeSupportedByName('currentHue')) { let lastRequestedHue = 0; let lastRequestedSaturation = 0; - let lastRequestTimeout: NodeJS.Timeout; this.bridgedDevice.addCommandHandler('moveToHue', async ({ request: request, attributes: attributes }) => { this.log.debug(`Command moveToHue called for ${this.ien}${device.friendly_name}${rs}${db} request: ${request.hue} attributes: hue ${attributes.currentHue?.getLocal()} saturation ${attributes.currentSaturation?.getLocal()} colorMode ${attributes.colorMode.getLocal()}`); attributes.colorMode.setLocal(ColorControl.ColorMode.CurrentHueAndCurrentSaturation); lastRequestedHue = request.hue; - lastRequestTimeout = setTimeout(() => { - clearTimeout(lastRequestTimeout); + this.colorTimeout = setTimeout(() => { + clearTimeout(this.colorTimeout); const rgb = color.hslColorToRgbColor((request.hue / 254) * 360, (lastRequestedSaturation / 254) * 100, 50); const payload: Payload = { color: { r: rgb.r, g: rgb.g, b: rgb.b } }; if (this.transition && request.transitionTime && request.transitionTime / 10 >= 1) payload['transition'] = Math.round(request.transitionTime / 10); @@ -1001,8 +1093,8 @@ export class ZigbeeDevice extends ZigbeeEntity { this.log.debug(`Command moveToSaturation called for ${this.ien}${device.friendly_name}${rs}${db} request: ${request.saturation} attributes: hue ${attributes.currentHue?.getLocal()} saturation ${attributes.currentSaturation?.getLocal()} colorMode ${attributes.colorMode.getLocal()}`); attributes.colorMode.setLocal(ColorControl.ColorMode.CurrentHueAndCurrentSaturation); lastRequestedSaturation = request.saturation; - lastRequestTimeout = setTimeout(() => { - clearTimeout(lastRequestTimeout); + this.colorTimeout = setTimeout(() => { + clearTimeout(this.colorTimeout); const rgb = color.hslColorToRgbColor((lastRequestedHue / 254) * 360, (request.saturation / 254) * 100, 50); const payload: Payload = { color: { r: rgb.r, g: rgb.g, b: rgb.b } }; if (this.transition && request.transitionTime && request.transitionTime / 10 >= 1) payload['transition'] = Math.round(request.transitionTime / 10); @@ -1055,13 +1147,13 @@ export class ZigbeeDevice extends ZigbeeEntity { this.bridgedDevice.addCommandHandler('lockDoor', async ({ request: request, attributes: attributes }) => { this.log.debug(`Command lockDoor called for ${this.ien}${device.friendly_name}${rs}${db}`, request); attributes.lockState?.setLocal(DoorLock.LockState.Locked); - if (!this.bridgedDevice?.isRouter) this.publishCommand('lockDoor', device.friendly_name, { state: 'LOCK' }); + if (!this.isRouter) this.publishCommand('lockDoor', device.friendly_name, { state: 'LOCK' }); else this.publishCommand('permit_join: false', 'bridge/request/permit_join', { value: false }); }); this.bridgedDevice.addCommandHandler('unlockDoor', async ({ request: request, attributes: attributes }) => { this.log.debug(`Command unlockDoor called for ${this.ien}${device.friendly_name}${rs}${db}`, request); attributes.lockState?.setLocal(DoorLock.LockState.Unlocked); - if (!this.bridgedDevice?.isRouter) this.publishCommand('unlockDoor', device.friendly_name, { state: 'UNLOCK' }); + if (!this.isRouter) this.publishCommand('unlockDoor', device.friendly_name, { state: 'UNLOCK' }); else this.publishCommand('permit_join: true', 'bridge/request/permit_join', { value: true }); }); } @@ -1095,99 +1187,30 @@ export class ZigbeeDevice extends ZigbeeEntity { this.log.debug(`Subscribe systemMode called for ${this.ien}${device.friendly_name}${rs}${db} with:`, value); const system_mode = value === Thermostat.SystemMode.Off ? 'off' : value === Thermostat.SystemMode.Heat ? 'heat' : 'cool'; this.publishCommand('SystemMode', device.friendly_name, { system_mode }); - if (this.bridgedDevice) this.bridgedDevice.noUpdate = true; - setTimeout(() => { - if (this.bridgedDevice) this.bridgedDevice.noUpdate = false; + this.noUpdate = true; + this.thermostatTimeout = setTimeout(() => { + this.noUpdate = false; }, 10 * 1000); }); thermostat.subscribeOccupiedHeatingSetpointAttribute(async (value) => { this.log.debug(`Subscribe occupiedHeatingSetpoint called for ${this.ien}${device.friendly_name}${rs}${db} with:`, value); if (this.propertyMap.has('current_heating_setpoint')) this.publishCommand('OccupiedHeatingSetpoint', device.friendly_name, { current_heating_setpoint: Math.round(value / 100) }); else if (this.propertyMap.has('occupied_heating_setpoint')) this.publishCommand('OccupiedHeatingSetpoint', device.friendly_name, { occupied_heating_setpoint: Math.round(value / 100) }); - if (this.bridgedDevice) this.bridgedDevice.noUpdate = true; - setTimeout(() => { - if (this.bridgedDevice) this.bridgedDevice.noUpdate = false; + if (this.bridgedDevice) this.noUpdate = true; + this.thermostatTimeout = setTimeout(() => { + if (this.bridgedDevice) this.noUpdate = false; }, 10 * 1000); }); thermostat.subscribeOccupiedCoolingSetpointAttribute(async (value) => { this.log.debug(`Subscribe occupiedCoolingSetpoint called for ${this.ien}${device.friendly_name}${rs}${db} with:`, value); if (this.propertyMap.has('current_cooling_setpoint')) this.publishCommand('OccupiedCoolingSetpoint', device.friendly_name, { current_cooling_setpoint: Math.round(value / 100) }); else if (this.propertyMap.has('occupied_cooling_setpoint')) this.publishCommand('OccupiedCoolingSetpoint', device.friendly_name, { occupied_cooling_setpoint: Math.round(value / 100) }); - if (this.bridgedDevice) this.bridgedDevice.noUpdate = true; - setTimeout(() => { - if (this.bridgedDevice) this.bridgedDevice.noUpdate = false; + if (this.bridgedDevice) this.noUpdate = true; + this.thermostatTimeout = setTimeout(() => { + if (this.bridgedDevice) this.noUpdate = false; }, 10 * 1000); }); } } } } - -export function addTagList(endpoint: Endpoint, mfgCode: VendorId | null, namespaceId: number, tag: number, label: string | null = null) { - const descriptor = endpoint.getClusterServerById(DescriptorCluster.id); - if (!descriptor) return; - // console.log('addTagList', namespaceId, tag, label); - // console.log('original descriptor', descriptor); - - endpoint.addClusterServer( - ClusterServer( - DescriptorCluster.with(Descriptor.Feature.TagList), - { - tagList: [{ mfgCode, namespaceId, tag, label }], - deviceTypeList: [...descriptor.attributes.deviceTypeList.getLocal()], - serverList: [...descriptor.attributes.serverList.getLocal()], - clientList: [...descriptor.attributes.clientList.getLocal()], - partsList: [...descriptor.attributes.partsList.getLocal()], - }, - {}, - {}, - ), - ); - - // descriptor = endpoint.getClusterServerById(DescriptorCluster.id); - // console.log('new descriptor', descriptor); -} - -export class BridgedBaseDevice extends MatterbridgeDevice { - public hasEndpoints = false; - public isRouter = false; - public noUpdate = false; - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - constructor(entity: ZigbeeEntity, definition: AtLeastOne, includeServerList: ClusterId[] = [], includeClientList?: ClusterId[]) { - super(definition, undefined, entity.log.logLevel === LogLevel.DEBUG); - this.addClusterServerFromList(this, includeServerList); - - // Add bridgedNode device type and BridgedDeviceBasicInformation cluster - this.addDeviceType(bridgedNode); - if (entity.isDevice && entity.device && entity.device.friendly_name === 'Coordinator') { - this.createDefaultBridgedDeviceBasicInformationClusterServer(entity.device.friendly_name, entity.serial, 0xfff1, 'zigbee2MQTT', 'Coordinator'); - } else if (entity.isDevice && entity.device) { - this.createDefaultBridgedDeviceBasicInformationClusterServer(entity.device.friendly_name, entity.serial, 0xfff1, entity.device.definition ? entity.device.definition.vendor : entity.device.manufacturer, entity.device.definition ? entity.device.definition.model : entity.device.model_id); - } else if (entity.isGroup && entity.group) { - this.createDefaultBridgedDeviceBasicInformationClusterServer(entity.group.friendly_name, entity.serial, 0xfff1, 'zigbee2MQTT', 'Group'); - } - - // Add powerSource device type and PowerSource cluster - this.addDeviceType(powerSource); - if (entity.isDevice) { - if (entity.device?.power_source === 'Battery') this.createDefaultPowerSourceReplaceableBatteryClusterServer(100, PowerSource.BatChargeLevel.Ok); - else this.createDefaultPowerSourceWiredClusterServer(); - } else if (entity.isGroup) { - this.createDefaultPowerSourceWiredClusterServer(); - } - } - - configure() { - if (this.getClusterServerById(WindowCovering.Cluster.id)) { - this.log.info(`Configuring ${this.deviceName} WindowCovering cluster`); - this.setWindowCoveringTargetAsCurrentAndStopped(); - } - if (this.getClusterServerById(DoorLock.Cluster.id)) { - this.log.info(`Configuring ${this.deviceName} DoorLock cluster`); - const state = this.getClusterServerById(DoorLock.Cluster.id)?.getLockStateAttribute(); - if (state === DoorLock.LockState.Locked) this.getClusterServer(DoorLockCluster)?.triggerLockOperationEvent({ lockOperationType: DoorLock.LockOperationType.Lock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }); - if (state === DoorLock.LockState.Unlocked) this.getClusterServer(DoorLockCluster)?.triggerLockOperationEvent({ lockOperationType: DoorLock.LockOperationType.Unlock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }); - } - } -} diff --git a/src/index.test.ts b/src/index.test.ts index 1e4a1d8..e071fa9 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ import { Matterbridge, PlatformConfig } from 'matterbridge'; import { AnsiLogger } from 'matterbridge/logger'; import { ZigbeePlatform } from './platform.js'; @@ -15,18 +16,46 @@ describe('initializePlugin', () => { matterbridgeDirectory: '', matterbridgePluginDirectory: 'temp', systemInformation: { ipv4Address: undefined }, - matterbridgeVersion: '1.6.1', + matterbridgeVersion: '1.6.2', removeAllBridgedDevices: jest.fn(), } as unknown as Matterbridge; - mockLog = { error: jest.fn(), warn: jest.fn(), info: jest.fn(), debug: jest.fn() } as unknown as AnsiLogger; + mockLog = { + fatal: jest.fn((message) => { + // console.log(`Fatal: ${message}`); + }), + error: jest.fn((message) => { + // console.log(`Error: ${message}`); + }), + warn: jest.fn((message) => { + // console.log(`Warn: ${message}`); + }), + notice: jest.fn((message) => { + // console.log(`Notice: ${message}`); + }), + info: jest.fn((message) => { + // console.log(`Info: ${message}`); + }), + debug: jest.fn((message) => { + // console.log(`Debug: ${message}`); + }), + } as unknown as AnsiLogger; mockConfig = { - 'name': 'matterbridge-test', + 'name': 'matterbridge-zigbee2mqtt', 'type': 'DynamicPlatform', - 'noDevices': false, - 'throwLoad': false, - 'throwStart': false, - 'throwConfigure': false, - 'throwShutdown': false, + 'topic': 'zigbee2mqtt', + 'host': 'localhost', + 'port': -1883, + 'protocolVersion': 5, + 'username': undefined, + 'password': undefined, + 'blackList': [], + 'whiteList': [], + 'switchList': [], + 'lightList': [], + 'outletList': [], + 'featureBlackList': ['device_temperature', 'consumption', 'update', 'update_available', 'power_outage_count', 'indicator_mode', 'do_not_disturb', 'color_temp_startup'], + 'deviceFeatureBlackList': {}, + 'postfixHostname': true, 'unregisterOnShutdown': false, 'delayStart': false, } as PlatformConfig; diff --git a/src/mock/bridge-payloads.txt b/src/mock/bridge-payloads.txt new file mode 100644 index 0000000..da12fc0 --- /dev/null +++ b/src/mock/bridge-payloads.txt @@ -0,0 +1,702 @@ +{"entity":"Climate sensor","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Contact sensor","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Light sensor","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Motion sensor","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Occupancy sensor","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Occupancy sensor P1","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Aqara switch T1","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Aqara Dual relay module T2","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Window shutter","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Scenes button","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Moes thermo","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Moes switch double","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Leak sensor","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Gledopto RGBCTT light I","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Smart button","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Develco air quality","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Aqara switch no neutral","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Mini luminance motion sensor","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Moes RGBCCT light bulb","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Inrr RGBCTT light","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Eglo RGBCTT light","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Carbon Monoxide sensor","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Vibration sensor","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Presence sensor","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Moes dimmer double","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Smart air box 2 (_TZE200_mja3fuja)","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"EARU Circuit Breaker 16A","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"EARU Circuit Breaker 32A","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Gledopto RGBCTT light II","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Gledopto GL-SD-001","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Moes RGBCCT light bulb III","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Moes RGBCCT light bulb II","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"At home","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Is dark","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Is night","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Sleeping","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Guests","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Master motion","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Lights","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Switches","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Covers","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Thermostats","service":"availability","payload":"{\"state\":\"online\"}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:13:46+01:00\",\"linkquality\":97,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:13:46+01:00\",\"linkquality\":207,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":360,\"formaldehyd\":2,\"humidity\":33.9,\"last_seen\":\"2024-11-23T15:13:45+01:00\",\"linkquality\":91,\"temperature\":21.7,\"voc\":1}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.9212030029296876,\"current\":0.02,\"device_temperature\":41,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:13:48+01:00\",\"linkquality\":244,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":230.42}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:13:48+01:00\",\"linkquality\":196,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"EARU Circuit Breaker 32A","payload":"{\"current\":0.05,\"energy\":4.26,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:13:53+01:00\",\"linkquality\":234,\"power\":6,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":31.96,\"voltage\":233}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":362,\"formaldehyd\":1,\"humidity\":33.8,\"last_seen\":\"2024-11-23T15:13:53+01:00\",\"linkquality\":97,\"temperature\":21.8,\"voc\":1}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":363,\"formaldehyd\":1,\"humidity\":33.8,\"last_seen\":\"2024-11-23T15:13:55+01:00\",\"linkquality\":91,\"temperature\":21.7,\"voc\":2}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:13:56+01:00\",\"linkquality\":201,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.9212030029296876,\"current\":0.02,\"device_temperature\":41,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:14:01+01:00\",\"linkquality\":240,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":230.42}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":364,\"formaldehyd\":2,\"humidity\":33.9,\"last_seen\":\"2024-11-23T15:14:03+01:00\",\"linkquality\":87,\"temperature\":21.9,\"voc\":1}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":362,\"formaldehyd\":2,\"humidity\":33.9,\"last_seen\":\"2024-11-23T15:14:06+01:00\",\"linkquality\":87,\"temperature\":22,\"voc\":4}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2959,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:10+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2959,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:11+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2959,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:11+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":362,\"formaldehyd\":1,\"humidity\":34.2,\"last_seen\":\"2024-11-23T15:14:11+01:00\",\"linkquality\":91,\"temperature\":22,\"voc\":3}"} +{"entity":"Smart air box 2 (_TZE200_mja3fuja)","payload":"{\"co2\":358,\"formaldehyd\":1,\"humidity\":31,\"last_seen\":\"2024-11-23T15:14:12+01:00\",\"linkquality\":255,\"temperature\":23.3,\"voc\":0}"} +{"entity":"Aqara switch T1","service":"get","payload":"{ \"state\": \"\", \"power\": \"\", \"power_outage_memory\": \"\", \"led_disabled_night\": \"\", \"switch_type\": \"\" }"} +{"entity":"Aqara Dual relay module T2","service":"get","payload":"{ \"state_l1\": \"\", \"state_l2\": \"\", \"switch_type\": \"\", \"power_on_behavior\": \"\", \"operation_mode_l1\": \"\", \"operation_mode_l2\": \"\", \"interlock\": \"\", \"mode\": \"\", \"pulse_length\": \"\" }"} +{"entity":"Window shutter","service":"get","payload":"{ \"position\": \"\", \"calibration_time\": \"\", \"motor_reversal\": \"\" }"} +{"entity":"Moes switch double","service":"get","payload":"{ \"state_l1\": \"\", \"state_l2\": \"\", \"power_on_behavior\": \"\" }"} +{"entity":"Gledopto RGBCTT light I","service":"get","payload":"{ \"state\": \"\", \"brightness\": \"\", \"color_temp\": \"\", \"color_temp_startup\": \"\", \"color\": \"\" }"} +{"entity":"Aqara switch no neutral","service":"get","payload":"{ \"state\": \"\", \"power_outage_memory\": \"\", \"switch_type\": \"\" }"} +{"entity":"Moes RGBCCT light bulb","service":"get","payload":"{ \"state\": \"\", \"brightness\": \"\", \"color_temp\": \"\", \"color\": \"\" }"} +{"entity":"Inrr RGBCTT light","service":"get","payload":"{ \"state\": \"\", \"brightness\": \"\", \"color_temp\": \"\", \"color_temp_startup\": \"\", \"color\": \"\", \"power_on_behavior\": \"\" }"} +{"entity":"Eglo RGBCTT light","service":"get","payload":"{ \"state\": \"\", \"brightness\": \"\", \"color_temp\": \"\", \"color_temp_startup\": \"\", \"color\": \"\", \"power_on_behavior\": \"\" }"} +{"entity":"EARU Circuit Breaker 16A","service":"get","payload":"{ \"state\": \"\", \"power_outage_memory\": \"\", \"indicator_mode\": \"\" }"} +{"entity":"EARU Circuit Breaker 32A","service":"get","payload":"{ \"state\": \"\", \"power_outage_memory\": \"\", \"indicator_mode\": \"\" }"} +{"entity":"Gledopto RGBCTT light II","service":"get","payload":"{ \"state\": \"\", \"brightness\": \"\", \"color_temp\": \"\", \"color_temp_startup\": \"\", \"color\": \"\" }"} +{"entity":"Gledopto GL-SD-001","service":"get","payload":"{ \"state\": \"\", \"brightness\": \"\" }"} +{"entity":"Moes RGBCCT light bulb III","service":"get","payload":"{ \"state\": \"\", \"brightness\": \"\", \"color_temp\": \"\", \"color\": \"\" }"} +{"entity":"Moes RGBCCT light bulb II","service":"get","payload":"{ \"state\": \"\", \"brightness\": \"\", \"color_temp\": \"\", \"color\": \"\" }"} +{"entity":"At home","service":"get","payload":"{ \"state\": \"\" }"} +{"entity":"Is dark","service":"get","payload":"{ \"state\": \"\" }"} +{"entity":"Is night","service":"get","payload":"{ \"state\": \"\" }"} +{"entity":"Sleeping","service":"get","payload":"{ \"state\": \"\" }"} +{"entity":"Guests","service":"get","payload":"{ \"state\": \"\" }"} +{"entity":"Master motion","service":"get","payload":"{ \"state\": \"\" }"} +{"entity":"Lights","service":"get","payload":"{ \"state\": \"\" }"} +{"entity":"Switches","service":"get","payload":"{ \"state\": \"\" }"} +{"entity":"Covers","service":"get","payload":"{ \"state\": \"\" }"} +{"entity":"Thermostats","service":"get","payload":"{ \"state\": \"\" }"} +{"entity":"Aqara switch T1","payload":"{\"consumption\":0,\"current\":0,\"device_temperature\":23,\"energy\":0,\"last_seen\":\"2024-11-23T15:14:13+01:00\",\"led_disabled_night\":false,\"linkquality\":207,\"power\":0,\"power_outage_count\":1,\"power_outage_memory\":false,\"state\":\"OFF\",\"switch_type\":\"toggle\",\"voltage\":235.22}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.9212030029296876,\"current\":0.02,\"device_temperature\":41,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:14:14+01:00\",\"linkquality\":234,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":230.42}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:14:14+01:00\",\"linkquality\":147,\"motor_reversal\":\"OFF\",\"moving\":\"STOP\",\"position\":0,\"state\":\"CLOSE\"}"} +{"entity":"Moes switch double","payload":"{\"last_seen\":\"2024-11-23T15:14:14+01:00\",\"linkquality\":240,\"power_on_behavior\":\"off\",\"power_on_behavior_l1\":\"off\",\"state_l1\":\"OFF\",\"state_l2\":\"OFF\"}"} +{"entity":"Lights","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"state\":\"OFF\"}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:14:14+01:00\",\"linkquality\":201,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Moes RGBCCT light bulb","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"last_seen\":\"2024-11-23T15:14:14+01:00\",\"linkquality\":174,\"state\":\"OFF\"}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:14:14+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":201,\"power_on_behavior\":\"off\",\"state\":\"OFF\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Aqara switch no neutral","payload":"{\"device_temperature\":30,\"last_seen\":\"2024-11-23T15:14:14+01:00\",\"linkquality\":255,\"power_outage_count\":0,\"power_outage_memory\":false,\"state\":\"OFF\",\"switch_type\":\"toggle\"}"} +{"entity":"Eglo RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":370,\"last_seen\":\"2024-11-23T15:14:14+01:00\",\"linkquality\":240,\"power_on_behavior\":\"off\",\"state\":\"OFF\"}"} +{"entity":"EARU Circuit Breaker 16A","payload":"{\"current\":0,\"energy\":0.4,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:14:14+01:00\",\"linkquality\":229,\"power\":0,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":29.96,\"voltage\":234}"} +{"entity":"EARU Circuit Breaker 32A","payload":"{\"current\":0.05,\"energy\":4.26,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:14:14+01:00\",\"linkquality\":184,\"power\":6,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":31.96,\"voltage\":233}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:14:15+01:00\",\"linkquality\":108,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":0,\"last_seen\":\"2024-11-23T15:14:15+01:00\",\"linkquality\":255,\"state\":\"OFF\"}"} +{"entity":"Moes RGBCCT light bulb III","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:14:15+01:00\",\"linkquality\":213,\"state\":\"OFF\"}"} +{"entity":"Moes RGBCCT light bulb II","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:14:15+01:00\",\"linkquality\":229,\"state\":\"OFF\"}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:14:17+01:00\",\"linkquality\":120,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:14:17+01:00\",\"linkquality\":196,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Moes dimmer double","payload":"{\"brightness_l1\":254,\"brightness_l2\":254,\"last_seen\":\"2024-11-23T15:14:17+01:00\",\"linkquality\":153,\"state_l1\":\"OFF\",\"state_l2\":\"OFF\"}"} +{"entity":"Smart air box 2 (_TZE200_mja3fuja)","payload":"{\"co2\":358,\"formaldehyd\":1,\"humidity\":31,\"last_seen\":\"2024-11-23T15:14:18+01:00\",\"linkquality\":255,\"temperature\":23.3,\"voc\":0}"} +{"entity":"Lights","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"state\":\"OFF\"}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:14:19+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":190,\"power_on_behavior\":\"off\",\"state\":\"OFF\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Moes RGBCCT light bulb III","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:14:19+01:00\",\"linkquality\":223,\"state\":\"OFF\"}"} +{"entity":"Moes RGBCCT light bulb","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"last_seen\":\"2024-11-23T15:14:19+01:00\",\"linkquality\":174,\"state\":\"OFF\"}"} +{"entity":"Moes RGBCCT light bulb II","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:14:19+01:00\",\"linkquality\":229,\"state\":\"OFF\"}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:14:19+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":196,\"power_on_behavior\":\"off\",\"state\":\"OFF\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:14:19+01:00\",\"linkquality\":207,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:19+01:00\",\"linkquality\":207,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Occupancy sensor P1","payload":"{\"battery\":100,\"detection_interval\":30,\"device_temperature\":25,\"illuminance\":571,\"last_seen\":\"2024-11-23T15:14:19+01:00\",\"linkquality\":255,\"motion_sensitivity\":\"medium\",\"occupancy\":true,\"power_outage_count\":34,\"trigger_indicator\":false,\"voltage\":3032}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:19+01:00\",\"linkquality\":196,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":207,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":207,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Moes switch double","payload":"{\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":240,\"power_on_behavior\":\"off\",\"power_on_behavior_l1\":\"off\",\"state_l1\":\"OFF\",\"state_l2\":\"OFF\"}"} +{"entity":"Moes switch double","payload":"{\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":240,\"power_on_behavior\":\"off\",\"power_on_behavior_l1\":\"off\",\"state_l1\":\"OFF\",\"state_l2\":\"OFF\"}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":180,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.9212030029296876,\"current\":0.02,\"device_temperature\":41,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":234,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":230.42}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.9212030029296876,\"current\":0.02,\"device_temperature\":41,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":234,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":230.42}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":213,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2959,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":223,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2959,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":213,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":213,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.9212030029296876,\"current\":0.02,\"device_temperature\":41,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":234,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":230.42}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":141,\"motor_reversal\":\"OFF\",\"moving\":\"STOP\",\"position\":0,\"state\":\"CLOSE\"}"} +{"entity":"Moes switch double","payload":"{\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":213,\"power_on_behavior\":\"off\",\"power_on_behavior_l1\":\"off\",\"state_l1\":\"OFF\",\"state_l2\":\"OFF\"}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2959,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":213,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":213,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Moes RGBCCT light bulb","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"last_seen\":\"2024-11-23T15:14:20+01:00\",\"linkquality\":157,\"state\":\"OFF\"}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"linkquality\":207,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":217,\"power_on_behavior\":\"off\",\"state\":\"OFF\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Eglo RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":370,\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"linkquality\":240,\"power_on_behavior\":\"off\",\"state\":\"OFF\"}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"linkquality\":196,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Aqara switch no neutral","payload":"{\"device_temperature\":30,\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"linkquality\":255,\"power_outage_count\":0,\"power_outage_memory\":false,\"state\":\"OFF\",\"switch_type\":\"toggle\"}"} +{"entity":"EARU Circuit Breaker 16A","payload":"{\"current\":0,\"energy\":0.4,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"linkquality\":207,\"power\":0,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":29.96,\"voltage\":234}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2959,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"EARU Circuit Breaker 32A","payload":"{\"current\":0.05,\"energy\":4.26,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"linkquality\":124,\"power\":6,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":31.96,\"voltage\":233}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"linkquality\":196,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"linkquality\":168,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"linkquality\":201,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Moes RGBCCT light bulb III","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"linkquality\":201,\"state\":\"OFF\"}"} +{"entity":"Aqara switch T1","payload":"{\"consumption\":0,\"current\":0,\"device_temperature\":23,\"energy\":0,\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"led_disabled_night\":false,\"linkquality\":229,\"power\":0,\"power_outage_count\":1,\"power_outage_memory\":false,\"state\":\"OFF\",\"switch_type\":\"toggle\",\"voltage\":235.22}"} +{"entity":"Moes RGBCCT light bulb II","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"linkquality\":223,\"state\":\"OFF\"}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"linkquality\":207,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"linkquality\":207,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2959,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2959,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"linkquality\":108,\"motor_reversal\":\"OFF\",\"moving\":\"STOP\",\"position\":0,\"state\":\"CLOSE\"}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.9212030029296876,\"current\":0.02,\"device_temperature\":41,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:14:21+01:00\",\"linkquality\":229,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":230.42}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:22+01:00\",\"linkquality\":196,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Moes switch double","payload":"{\"last_seen\":\"2024-11-23T15:14:22+01:00\",\"linkquality\":223,\"power_on_behavior\":\"off\",\"power_on_behavior_l1\":\"off\",\"state_l1\":\"OFF\",\"state_l2\":\"OFF\"}"} +{"entity":"Lights","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"state\":\"OFF\"}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:14:22+01:00\",\"linkquality\":213,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Lights","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"state\":\"OFF\"}"} +{"entity":"Moes RGBCCT light bulb","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"last_seen\":\"2024-11-23T15:14:22+01:00\",\"linkquality\":124,\"state\":\"OFF\"}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:22+01:00\",\"linkquality\":196,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Lights","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"state\":\"OFF\"}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:14:22+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":223,\"power_on_behavior\":\"off\",\"state\":\"OFF\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Eglo RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":370,\"last_seen\":\"2024-11-23T15:14:22+01:00\",\"linkquality\":250,\"power_on_behavior\":\"off\",\"state\":\"OFF\"}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:22+01:00\",\"linkquality\":213,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2959,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:22+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":0,\"last_seen\":\"2024-11-23T15:14:22+01:00\",\"linkquality\":255,\"state\":\"OFF\"}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:22+01:00\",\"linkquality\":213,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"EARU Circuit Breaker 16A","payload":"{\"current\":0,\"energy\":0.4,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:14:22+01:00\",\"linkquality\":217,\"power\":0,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":29.96,\"voltage\":234}"} +{"entity":"EARU Circuit Breaker 32A","payload":"{\"current\":0.05,\"energy\":4.26,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:14:22+01:00\",\"linkquality\":103,\"power\":6,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":31.96,\"voltage\":233}"} +{"entity":"Aqara switch no neutral","payload":"{\"device_temperature\":30,\"last_seen\":\"2024-11-23T15:14:22+01:00\",\"linkquality\":255,\"power_outage_count\":0,\"power_outage_memory\":false,\"state\":\"OFF\",\"switch_type\":\"toggle\"}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2959,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:22+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:14:23+01:00\",\"linkquality\":180,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Lights","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"state\":\"OFF\"}"} +{"entity":"Moes RGBCCT light bulb III","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:14:23+01:00\",\"linkquality\":201,\"state\":\"OFF\"}"} +{"entity":"Moes RGBCCT light bulb II","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:14:23+01:00\",\"linkquality\":223,\"state\":\"OFF\"}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2959,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:23+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.9212030029296876,\"current\":0.02,\"device_temperature\":41,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:14:23+01:00\",\"linkquality\":207,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":230.42}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:14:23+01:00\",\"linkquality\":213,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Moes RGBCCT light bulb","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"last_seen\":\"2024-11-23T15:14:23+01:00\",\"linkquality\":180,\"state\":\"OFF\"}"} +{"entity":"Lights","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"state\":\"OFF\"}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:14:23+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":196,\"power_on_behavior\":\"off\",\"state\":\"OFF\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Eglo RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":370,\"last_seen\":\"2024-11-23T15:14:23+01:00\",\"linkquality\":250,\"power_on_behavior\":\"off\",\"state\":\"OFF\"}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:14:23+01:00\",\"linkquality\":174,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Lights","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"state\":\"OFF\"}"} +{"entity":"Moes RGBCCT light bulb III","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:14:23+01:00\",\"linkquality\":207,\"state\":\"OFF\"}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2959,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:23+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Aqara switch T1","payload":"{\"consumption\":0,\"current\":0,\"device_temperature\":23,\"energy\":0,\"last_seen\":\"2024-11-23T15:14:24+01:00\",\"led_disabled_night\":false,\"linkquality\":234,\"power\":0,\"power_outage_count\":1,\"power_outage_memory\":false,\"state\":\"OFF\",\"switch_type\":\"toggle\",\"voltage\":235.22}"} +{"entity":"Moes RGBCCT light bulb II","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:14:24+01:00\",\"linkquality\":217,\"state\":\"OFF\"}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.9212030029296876,\"current\":0.02,\"device_temperature\":41,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:14:24+01:00\",\"linkquality\":196,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":230.42}"} +{"entity":"Lights","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"state\":\"OFF\"}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:14:24+01:00\",\"linkquality\":207,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Lights","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"state\":\"OFF\"}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:14:24+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":201,\"power_on_behavior\":\"off\",\"state\":\"OFF\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Eglo RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":370,\"last_seen\":\"2024-11-23T15:14:24+01:00\",\"linkquality\":255,\"power_on_behavior\":\"off\",\"state\":\"OFF\"}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:14:24+01:00\",\"linkquality\":180,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.9212030029296876,\"current\":0.02,\"device_temperature\":41,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:14:24+01:00\",\"linkquality\":201,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":230.42}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:14:24+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":213,\"power_on_behavior\":\"off\",\"state\":\"OFF\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Eglo RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":370,\"last_seen\":\"2024-11-23T15:14:24+01:00\",\"linkquality\":255,\"power_on_behavior\":\"off\",\"state\":\"OFF\"}"} +{"entity":"Aqara switch T1","payload":"{\"consumption\":0,\"current\":0,\"device_temperature\":23,\"energy\":0,\"last_seen\":\"2024-11-23T15:14:24+01:00\",\"led_disabled_night\":false,\"linkquality\":240,\"power\":0,\"power_outage_count\":1,\"power_outage_memory\":false,\"state\":\"OFF\",\"switch_type\":\"toggle\",\"voltage\":235.22}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.9212030029296876,\"current\":0.02,\"device_temperature\":41,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:14:24+01:00\",\"linkquality\":213,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":230.42}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.9212030029296876,\"current\":0.02,\"device_temperature\":41,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:14:25+01:00\",\"linkquality\":217,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":230.42}"} +{"entity":"Aqara switch T1","payload":"{\"consumption\":0,\"current\":0,\"device_temperature\":23,\"energy\":0,\"last_seen\":\"2024-11-23T15:14:25+01:00\",\"led_disabled_night\":false,\"linkquality\":234,\"power\":0,\"power_outage_count\":1,\"power_outage_memory\":false,\"state\":\"OFF\",\"switch_type\":\"toggle\",\"voltage\":235.22}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":364,\"formaldehyd\":2,\"humidity\":33.9,\"last_seen\":\"2024-11-23T15:14:25+01:00\",\"linkquality\":91,\"temperature\":21.7,\"voc\":4}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.9212030029296876,\"current\":0.02,\"device_temperature\":41,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:14:25+01:00\",\"linkquality\":213,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":230.42}"} +{"entity":"Moes dimmer double","payload":"{\"brightness_l1\":254,\"brightness_l2\":254,\"last_seen\":\"2024-11-23T15:14:29+01:00\",\"linkquality\":70,\"state_l1\":\"OFF\",\"state_l2\":\"OFF\"}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":364,\"formaldehyd\":4,\"humidity\":33.6,\"last_seen\":\"2024-11-23T15:14:28+01:00\",\"linkquality\":97,\"temperature\":22,\"voc\":4}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:14:31+01:00\",\"linkquality\":240,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":363,\"formaldehyd\":3,\"humidity\":33.7,\"last_seen\":\"2024-11-23T15:14:31+01:00\",\"linkquality\":103,\"temperature\":22,\"voc\":1}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":363,\"formaldehyd\":1,\"humidity\":33.8,\"last_seen\":\"2024-11-23T15:14:38+01:00\",\"linkquality\":103,\"temperature\":22,\"voc\":1}"} +{"entity":"Occupancy sensor","payload":"{\"battery\":100,\"device_temperature\":29,\"illuminance\":252,\"illuminance_lux\":252,\"last_seen\":\"2024-11-23T15:13:40+01:00\",\"linkquality\":91,\"occupancy\":false,\"power_outage_count\":548,\"voltage\":3035}"} +{"entity":"Occupancy sensor","payload":"{\"battery\":100,\"device_temperature\":29,\"illuminance\":247,\"illuminance_lux\":247,\"last_seen\":\"2024-11-23T15:14:42+01:00\",\"linkquality\":91,\"occupancy\":true,\"power_outage_count\":548,\"voltage\":3035}"} +{"entity":"Occupancy sensor","payload":"{\"battery\":100,\"device_temperature\":29,\"illuminance\":247,\"illuminance_lux\":247,\"last_seen\":\"2024-11-23T15:14:42+01:00\",\"linkquality\":91,\"occupancy\":true,\"power_outage_count\":548,\"voltage\":3035}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":366,\"formaldehyd\":4,\"humidity\":34.2,\"last_seen\":\"2024-11-23T15:14:41+01:00\",\"linkquality\":81,\"temperature\":21.9,\"voc\":1}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:14:44+01:00\",\"linkquality\":213,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:14:46+01:00\",\"linkquality\":184,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:14:46+01:00\",\"linkquality\":213,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2725,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:47+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2725,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:47+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":367,\"formaldehyd\":4,\"humidity\":33.7,\"last_seen\":\"2024-11-23T15:14:46+01:00\",\"linkquality\":87,\"temperature\":21.8,\"voc\":5}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":364,\"formaldehyd\":2,\"humidity\":33.7,\"last_seen\":\"2024-11-23T15:14:49+01:00\",\"linkquality\":91,\"temperature\":21.7,\"voc\":3}"} +{"entity":"Light sensor","payload":"{\"battery\":100,\"device_temperature\":25,\"illuminance\":23839,\"illuminance_lux\":242,\"last_seen\":\"2024-11-23T15:14:51+01:00\",\"linkquality\":255,\"power_outage_count\":34,\"state\":\"OFF\",\"voltage\":3068}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2842,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:52+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2842,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:52+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2842,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:54+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2842,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:55+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2842,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:55+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:57+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:14:57+01:00\",\"linkquality\":217,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:57+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:14:56+01:00\",\"linkquality\":213,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":365,\"formaldehyd\":1,\"humidity\":34,\"last_seen\":\"2024-11-23T15:14:56+01:00\",\"linkquality\":87,\"temperature\":21.9,\"voc\":1}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:58+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:59+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:14:59+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Light sensor","payload":"{\"battery\":100,\"device_temperature\":25,\"illuminance\":24669,\"illuminance_lux\":293,\"last_seen\":\"2024-11-23T15:15:01+01:00\",\"linkquality\":255,\"power_outage_count\":34,\"state\":\"OFF\",\"voltage\":3068}"} +{"entity":"Occupancy sensor P1","payload":"{\"battery\":100,\"detection_interval\":30,\"device_temperature\":25,\"illuminance\":564,\"last_seen\":\"2024-11-23T15:15:03+01:00\",\"linkquality\":255,\"motion_sensitivity\":\"medium\",\"occupancy\":true,\"power_outage_count\":34,\"trigger_indicator\":false,\"voltage\":3032}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":365,\"formaldehyd\":1,\"humidity\":33.8,\"last_seen\":\"2024-11-23T15:15:03+01:00\",\"linkquality\":103,\"temperature\":21.9,\"voc\":3}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":367,\"formaldehyd\":3,\"humidity\":34.2,\"last_seen\":\"2024-11-23T15:15:06+01:00\",\"linkquality\":81,\"temperature\":21.9,\"voc\":1}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:15:10+01:00\",\"linkquality\":234,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Smart air box 2 (_TZE200_mja3fuja)","payload":"{\"co2\":368,\"formaldehyd\":2,\"humidity\":31,\"last_seen\":\"2024-11-23T15:15:14+01:00\",\"linkquality\":255,\"temperature\":23.3,\"voc\":1}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:15:15+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:15:16+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":359,\"formaldehyd\":1,\"humidity\":33.7,\"last_seen\":\"2024-11-23T15:15:14+01:00\",\"linkquality\":81,\"temperature\":22,\"voc\":2}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:15:16+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:15:16+01:00\",\"linkquality\":196,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:15:16+01:00\",\"linkquality\":196,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":356,\"formaldehyd\":2,\"humidity\":33.6,\"last_seen\":\"2024-11-23T15:15:16+01:00\",\"linkquality\":81,\"temperature\":21.8,\"voc\":2}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:15:22+01:00\",\"linkquality\":234,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":358,\"formaldehyd\":2,\"humidity\":33.9,\"last_seen\":\"2024-11-23T15:15:21+01:00\",\"linkquality\":87,\"temperature\":21.7,\"voc\":2}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:15:24+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:15:25+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:15:25+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":359,\"formaldehyd\":2,\"humidity\":33.5,\"last_seen\":\"2024-11-23T15:15:24+01:00\",\"linkquality\":81,\"temperature\":21.8,\"voc\":3}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:15:26+01:00\",\"linkquality\":234,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":358,\"formaldehyd\":2,\"humidity\":34,\"last_seen\":\"2024-11-23T15:15:31+01:00\",\"linkquality\":91,\"temperature\":21.9,\"voc\":4}"} +{"entity":"EARU Circuit Breaker 32A","payload":"{\"current\":0.05,\"energy\":4.26,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:15:35+01:00\",\"linkquality\":207,\"power\":5,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":31.96,\"voltage\":234}"} +{"entity":"EARU Circuit Breaker 32A","payload":"{\"current\":0.05,\"energy\":4.26,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:15:35+01:00\",\"linkquality\":213,\"power\":5,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":31.96,\"voltage\":234}"} +{"entity":"EARU Circuit Breaker 32A","payload":"{\"current\":0.05,\"energy\":4.26,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:15:36+01:00\",\"linkquality\":213,\"power\":5,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":31.96,\"voltage\":234}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":360,\"formaldehyd\":3,\"humidity\":34.1,\"last_seen\":\"2024-11-23T15:15:34+01:00\",\"linkquality\":91,\"temperature\":21.8,\"voc\":1}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:15:39+01:00\",\"linkquality\":244,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":359,\"formaldehyd\":4,\"humidity\":34.1,\"last_seen\":\"2024-11-23T15:15:39+01:00\",\"linkquality\":87,\"temperature\":21.8,\"voc\":5}"} +{"entity":"Occupancy sensor","payload":"{\"battery\":100,\"device_temperature\":29,\"illuminance\":247,\"illuminance_lux\":247,\"last_seen\":\"2024-11-23T15:14:42+01:00\",\"linkquality\":91,\"occupancy\":false,\"power_outage_count\":548,\"voltage\":3035}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":359,\"formaldehyd\":1,\"humidity\":33.5,\"last_seen\":\"2024-11-23T15:15:41+01:00\",\"linkquality\":87,\"temperature\":22,\"voc\":5}"} +{"entity":"EARU Circuit Breaker 16A","payload":"{\"current\":0,\"energy\":0.4,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:15:44+01:00\",\"linkquality\":217,\"power\":0,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":29.96,\"voltage\":234}"} +{"entity":"Occupancy sensor P1","payload":"{\"battery\":100,\"detection_interval\":30,\"device_temperature\":25,\"illuminance\":566,\"last_seen\":\"2024-11-23T15:15:45+01:00\",\"linkquality\":255,\"motion_sensitivity\":\"medium\",\"occupancy\":true,\"power_outage_count\":34,\"trigger_indicator\":false,\"voltage\":3032}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:15:46+01:00\",\"linkquality\":157,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:15:46+01:00\",\"linkquality\":207,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:15:47+01:00\",\"linkquality\":213,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Develco air quality","payload":"{\"air_quality\":\"moderate\",\"battery\":20,\"humidity\":30,\"last_seen\":\"2024-11-23T15:15:51+01:00\",\"linkquality\":255,\"temperature\":22.7,\"update\":{\"installed_version\":262145,\"latest_version\":262145,\"state\":\"idle\"},\"voc\":1035,\"voltage\":2600}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":359,\"formaldehyd\":4,\"humidity\":33.5,\"last_seen\":\"2024-11-23T15:15:49+01:00\",\"linkquality\":81,\"temperature\":21.9,\"voc\":4}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:15:52+01:00\",\"linkquality\":240,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":358,\"formaldehyd\":2,\"humidity\":33.6,\"last_seen\":\"2024-11-23T15:15:52+01:00\",\"linkquality\":87,\"temperature\":21.8,\"voc\":4}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:15:54+01:00\",\"linkquality\":240,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"EARU Circuit Breaker 16A","payload":"{\"current\":0,\"energy\":0.4,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:15:55+01:00\",\"linkquality\":213,\"power\":0,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":29.96,\"voltage\":234}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:15:55+01:00\",\"linkquality\":213,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:15:58+01:00\",\"linkquality\":240,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"EARU Circuit Breaker 16A","payload":"{\"current\":0,\"energy\":0.4,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:15:59+01:00\",\"linkquality\":255,\"power\":0,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":29.96,\"voltage\":234}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":361,\"formaldehyd\":4,\"humidity\":33.9,\"last_seen\":\"2024-11-23T15:15:57+01:00\",\"linkquality\":87,\"temperature\":21.8,\"voc\":3}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":361,\"formaldehyd\":4,\"humidity\":33.7,\"last_seen\":\"2024-11-23T15:15:59+01:00\",\"linkquality\":87,\"temperature\":22.1,\"voc\":1}"} +{"entity":"EARU Circuit Breaker 16A","payload":"{\"current\":0,\"energy\":0.4,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:15:59+01:00\",\"linkquality\":255,\"power\":0,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":29.96,\"voltage\":234}"} +{"entity":"EARU Circuit Breaker 16A","payload":"{\"current\":0,\"energy\":0.4,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:15:59+01:00\",\"linkquality\":255,\"power\":0,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":29.96,\"voltage\":234}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:16:06+01:00\",\"linkquality\":240,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":361,\"formaldehyd\":1,\"humidity\":34,\"last_seen\":\"2024-11-23T15:16:07+01:00\",\"linkquality\":87,\"temperature\":21.8,\"voc\":4}"} +{"entity":"Smart air box 2 (_TZE200_mja3fuja)","payload":"{\"co2\":358,\"formaldehyd\":1,\"humidity\":31,\"last_seen\":\"2024-11-23T15:16:11+01:00\",\"linkquality\":255,\"temperature\":23.3,\"voc\":0}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":360,\"formaldehyd\":3,\"humidity\":33.6,\"last_seen\":\"2024-11-23T15:16:09+01:00\",\"linkquality\":81,\"temperature\":22.1,\"voc\":3}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:16:16+01:00\",\"linkquality\":157,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":361,\"formaldehyd\":4,\"humidity\":34,\"last_seen\":\"2024-11-23T15:16:14+01:00\",\"linkquality\":81,\"temperature\":22,\"voc\":5}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:16:16+01:00\",\"linkquality\":207,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:16:18+01:00\",\"linkquality\":244,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":363,\"formaldehyd\":3,\"humidity\":34.2,\"last_seen\":\"2024-11-23T15:16:17+01:00\",\"linkquality\":87,\"temperature\":22.1,\"voc\":2}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":363,\"formaldehyd\":3,\"humidity\":34.3,\"last_seen\":\"2024-11-23T15:16:24+01:00\",\"linkquality\":87,\"temperature\":22,\"voc\":2}"} +{"entity":"EARU Circuit Breaker 32A","payload":"{\"current\":0.05,\"energy\":4.26,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:16:28+01:00\",\"linkquality\":207,\"power\":5,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":31.96,\"voltage\":234}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":363,\"formaldehyd\":1,\"humidity\":33.8,\"last_seen\":\"2024-11-23T15:16:27+01:00\",\"linkquality\":87,\"temperature\":21.7,\"voc\":1}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:16:31+01:00\",\"linkquality\":240,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:16:33+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:16:34+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:16:34+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":363,\"formaldehyd\":1,\"humidity\":34.2,\"last_seen\":\"2024-11-23T15:16:32+01:00\",\"linkquality\":87,\"temperature\":21.7,\"voc\":3}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":361,\"formaldehyd\":3,\"humidity\":34,\"last_seen\":\"2024-11-23T15:16:35+01:00\",\"linkquality\":87,\"temperature\":21.9,\"voc\":3}"} +{"entity":"Occupancy sensor P1","payload":"{\"battery\":100,\"detection_interval\":30,\"device_temperature\":25,\"illuminance\":558,\"last_seen\":\"2024-11-23T15:16:38+01:00\",\"linkquality\":255,\"motion_sensitivity\":\"medium\",\"occupancy\":true,\"power_outage_count\":34,\"trigger_indicator\":false,\"voltage\":3032}"} +{"entity":"Occupancy sensor","payload":"{\"battery\":100,\"device_temperature\":29,\"illuminance\":243,\"illuminance_lux\":243,\"last_seen\":\"2024-11-23T15:16:38+01:00\",\"linkquality\":91,\"occupancy\":true,\"power_outage_count\":548,\"voltage\":3035}"} +{"entity":"Occupancy sensor","payload":"{\"battery\":100,\"device_temperature\":29,\"illuminance\":243,\"illuminance_lux\":243,\"last_seen\":\"2024-11-23T15:16:38+01:00\",\"linkquality\":87,\"occupancy\":true,\"power_outage_count\":548,\"voltage\":3035}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:16:38+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:16:39+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:16:39+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:16:44+01:00\",\"linkquality\":234,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":362,\"formaldehyd\":4,\"humidity\":33.6,\"last_seen\":\"2024-11-23T15:16:42+01:00\",\"linkquality\":97,\"temperature\":21.9,\"voc\":5}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:16:46+01:00\",\"linkquality\":180,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:16:46+01:00\",\"linkquality\":201,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":363,\"formaldehyd\":4,\"humidity\":34,\"last_seen\":\"2024-11-23T15:16:45+01:00\",\"linkquality\":97,\"temperature\":22.1,\"voc\":5}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":0,\"last_seen\":\"2024-11-23T15:16:47+01:00\",\"linkquality\":255,\"state\":\"OFF\"}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:16:51+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:16:52+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:16:52+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:16:54+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:16:55+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:16:55+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:16:56+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2939,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:16:56+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:16:57+01:00\",\"linkquality\":223,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:16:56+01:00\",\"linkquality\":207,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:16:58+01:00\",\"linkquality\":217,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Smart air box 2 (_TZE200_mja3fuja)","payload":"{\"co2\":358,\"formaldehyd\":1,\"humidity\":31,\"last_seen\":\"2024-11-23T15:16:59+01:00\",\"linkquality\":255,\"temperature\":23.3,\"voc\":0}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2861,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:17:02+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":363,\"formaldehyd\":3,\"humidity\":33.7,\"last_seen\":\"2024-11-23T15:17:00+01:00\",\"linkquality\":87,\"temperature\":22,\"voc\":3}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2861,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:17:02+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":363,\"formaldehyd\":1,\"humidity\":34.3,\"last_seen\":\"2024-11-23T15:17:03+01:00\",\"linkquality\":91,\"temperature\":21.8,\"voc\":3}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:17:05+01:00\",\"linkquality\":213,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:17:06+01:00\",\"linkquality\":207,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:17:06+01:00\",\"linkquality\":213,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:17:06+01:00\",\"linkquality\":223,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":365,\"formaldehyd\":3,\"humidity\":34.1,\"last_seen\":\"2024-11-23T15:17:07+01:00\",\"linkquality\":103,\"temperature\":21.8,\"voc\":2}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":365,\"formaldehyd\":3,\"humidity\":34.1,\"last_seen\":\"2024-11-23T15:17:07+01:00\",\"linkquality\":97,\"temperature\":21.9,\"voc\":2}"} +{"entity":"Occupancy sensor P1","payload":"{\"battery\":100,\"detection_interval\":30,\"device_temperature\":25,\"illuminance\":514,\"last_seen\":\"2024-11-23T15:17:07+01:00\",\"linkquality\":255,\"motion_sensitivity\":\"medium\",\"occupancy\":true,\"power_outage_count\":34,\"trigger_indicator\":false,\"voltage\":3032}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":365,\"formaldehyd\":3,\"humidity\":34.1,\"last_seen\":\"2024-11-23T15:17:08+01:00\",\"linkquality\":91,\"temperature\":21.9,\"voc\":2}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":364,\"formaldehyd\":1,\"humidity\":34.2,\"last_seen\":\"2024-11-23T15:17:10+01:00\",\"linkquality\":91,\"temperature\":22,\"voc\":3}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:17:12+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:17:12+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:17:16+01:00\",\"linkquality\":184,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:17:16+01:00\",\"linkquality\":207,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:17:19+01:00\",\"linkquality\":196,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":368,\"formaldehyd\":2,\"humidity\":34,\"last_seen\":\"2024-11-23T15:17:17+01:00\",\"linkquality\":103,\"temperature\":21.8,\"voc\":3}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":367,\"formaldehyd\":3,\"humidity\":33.6,\"last_seen\":\"2024-11-23T15:17:20+01:00\",\"linkquality\":108,\"temperature\":22.1,\"voc\":5}"} +{"entity":"Moes dimmer double","payload":"{\"brightness_l1\":254,\"brightness_l2\":254,\"last_seen\":\"2024-11-23T15:17:22+01:00\",\"linkquality\":108,\"state_l1\":\"OFF\",\"state_l2\":\"OFF\"}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":359,\"formaldehyd\":4,\"humidity\":33.9,\"last_seen\":\"2024-11-23T15:17:28+01:00\",\"linkquality\":108,\"temperature\":22,\"voc\":4}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:17:30+01:00\",\"linkquality\":196,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:17:35+01:00\",\"linkquality\":240,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:17:36+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":223,\"power_on_behavior\":\"off\",\"state\":\"OFF\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Occupancy sensor","payload":"{\"battery\":100,\"device_temperature\":29,\"illuminance\":243,\"illuminance_lux\":243,\"last_seen\":\"2024-11-23T15:16:38+01:00\",\"linkquality\":87,\"occupancy\":false,\"power_outage_count\":548,\"voltage\":3035}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:17:39+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":223,\"power_on_behavior\":\"off\",\"state\":\"OFF\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":356,\"formaldehyd\":4,\"humidity\":33.7,\"last_seen\":\"2024-11-23T15:17:38+01:00\",\"linkquality\":87,\"temperature\":22,\"voc\":2}"} +{"entity":"Occupancy sensor P1","payload":"{\"battery\":100,\"detection_interval\":30,\"device_temperature\":25,\"illuminance\":563,\"last_seen\":\"2024-11-23T15:17:42+01:00\",\"linkquality\":255,\"motion_sensitivity\":\"medium\",\"occupancy\":true,\"power_outage_count\":34,\"trigger_indicator\":false,\"voltage\":3032}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:17:42+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":223,\"power_on_behavior\":\"off\",\"state\":\"OFF\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:17:45+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":229,\"power_on_behavior\":\"off\",\"state\":\"OFF\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:17:46+01:00\",\"linkquality\":180,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:17:46+01:00\",\"linkquality\":190,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:17:47+01:00\",\"linkquality\":244,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":358,\"formaldehyd\":1,\"humidity\":33.7,\"last_seen\":\"2024-11-23T15:17:45+01:00\",\"linkquality\":91,\"temperature\":21.9,\"voc\":5}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:17:48+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":223,\"power_on_behavior\":\"off\",\"state\":\"OFF\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:17:47+01:00\",\"linkquality\":196,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:17:52+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:17:52+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:17:52+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Aqara switch no neutral","payload":"{\"device_temperature\":30,\"last_seen\":\"2024-11-23T15:17:54+01:00\",\"linkquality\":255,\"power_outage_count\":0,\"power_outage_memory\":false,\"state\":\"OFF\",\"switch_type\":\"toggle\"}"} +{"entity":"Aqara switch no neutral","payload":"{\"device_temperature\":30,\"last_seen\":\"2024-11-23T15:17:54+01:00\",\"linkquality\":255,\"power_outage_count\":0,\"power_outage_memory\":false,\"state\":\"ON\",\"switch_type\":\"toggle\"}"} +{"entity":"Aqara switch no neutral","payload":"{\"device_temperature\":30,\"last_seen\":\"2024-11-23T15:17:54+01:00\",\"linkquality\":255,\"power_outage_count\":0,\"power_outage_memory\":false,\"state\":\"ON\",\"switch_type\":\"toggle\"}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":357,\"formaldehyd\":4,\"humidity\":33.8,\"last_seen\":\"2024-11-23T15:17:53+01:00\",\"linkquality\":81,\"temperature\":22,\"voc\":1}"} +{"entity":"Aqara switch T1","payload":"{\"consumption\":0,\"current\":0,\"device_temperature\":23,\"energy\":0,\"last_seen\":\"2024-11-23T15:17:56+01:00\",\"led_disabled_night\":false,\"linkquality\":217,\"power\":0,\"power_outage_count\":1,\"power_outage_memory\":false,\"state\":\"OFF\",\"switch_type\":\"toggle\",\"voltage\":235.22}"} +{"entity":"Aqara switch T1","payload":"{\"consumption\":0,\"current\":0,\"device_temperature\":23,\"energy\":0,\"last_seen\":\"2024-11-23T15:17:56+01:00\",\"led_disabled_night\":false,\"linkquality\":217,\"power\":0,\"power_outage_count\":1,\"power_outage_memory\":false,\"state\":\"ON\",\"switch_type\":\"toggle\",\"voltage\":235.22}"} +{"entity":"Aqara switch T1","payload":"{\"consumption\":0,\"current\":0,\"device_temperature\":23,\"energy\":0,\"last_seen\":\"2024-11-23T15:17:56+01:00\",\"led_disabled_night\":false,\"linkquality\":217,\"power\":0,\"power_outage_count\":1,\"power_outage_memory\":false,\"state\":\"ON\",\"switch_type\":\"toggle\",\"voltage\":235.22}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:17:55+01:00\",\"linkquality\":196,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Develco air quality","payload":"{\"air_quality\":\"moderate\",\"battery\":20,\"humidity\":30,\"last_seen\":\"2024-11-23T15:17:57+01:00\",\"linkquality\":255,\"temperature\":22.7,\"update\":{\"installed_version\":262145,\"latest_version\":262145,\"state\":\"idle\"},\"voc\":1035,\"voltage\":2600}"} +{"entity":"EARU Circuit Breaker 32A","payload":"{\"current\":0.05,\"energy\":4.26,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:17:58+01:00\",\"linkquality\":207,\"power\":16,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":31.96,\"voltage\":234}"} +{"entity":"Aqara switch T1","payload":"{\"consumption\":0,\"current\":0,\"device_temperature\":23,\"energy\":0,\"last_seen\":\"2024-11-23T15:17:59+01:00\",\"led_disabled_night\":false,\"linkquality\":223,\"power\":5.44,\"power_outage_count\":1,\"power_outage_memory\":false,\"state\":\"ON\",\"switch_type\":\"toggle\",\"voltage\":235.22}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:18:00+01:00\",\"linkquality\":244,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Eglo RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":370,\"last_seen\":\"2024-11-23T15:18:01+01:00\",\"linkquality\":240,\"power_on_behavior\":\"off\",\"state\":\"OFF\"}"} +{"entity":"Lights","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"state\":\"ON\"}"} +{"entity":"Eglo RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":370,\"last_seen\":\"2024-11-23T15:18:01+01:00\",\"linkquality\":240,\"power_on_behavior\":\"off\",\"state\":\"ON\"}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:18:02+01:00\",\"linkquality\":250,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":1,\"last_seen\":\"2024-11-23T15:16:47+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":1,\"last_seen\":\"2024-11-23T15:18:03+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":1,\"last_seen\":\"2024-11-23T15:18:03+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":1,\"last_seen\":\"2024-11-23T15:18:03+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":1,\"last_seen\":\"2024-11-23T15:18:03+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":1,\"last_seen\":\"2024-11-23T15:18:03+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":1,\"last_seen\":\"2024-11-23T15:18:03+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":1,\"last_seen\":\"2024-11-23T15:18:03+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":1,\"last_seen\":\"2024-11-23T15:18:03+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":1,\"last_seen\":\"2024-11-23T15:18:03+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":1,\"last_seen\":\"2024-11-23T15:18:03+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":1,\"last_seen\":\"2024-11-23T15:18:03+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":1,\"last_seen\":\"2024-11-23T15:18:03+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":1,\"last_seen\":\"2024-11-23T15:18:03+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":1,\"last_seen\":\"2024-11-23T15:18:03+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:18:04+01:00\",\"linkquality\":190,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Lights","payload":"{\"brightness\":2,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"state\":\"ON\"}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":2,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:18:04+01:00\",\"linkquality\":190,\"state\":\"ON\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Lights","payload":"{\"brightness\":2,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"state\":\"ON\"}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":2,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:18:04+01:00\",\"linkquality\":184,\"state\":\"ON\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":358,\"formaldehyd\":4,\"humidity\":34.2,\"last_seen\":\"2024-11-23T15:18:03+01:00\",\"linkquality\":87,\"temperature\":22,\"voc\":2}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":1,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:18:06+01:00\",\"linkquality\":180,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":2,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:18:06+01:00\",\"linkquality\":180,\"state\":\"ON\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":2,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:18:06+01:00\",\"linkquality\":180,\"state\":\"ON\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:18:06+01:00\",\"linkquality\":250,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:18:08+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":223,\"power_on_behavior\":\"off\",\"state\":\"OFF\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Lights","payload":"{\"brightness\":2,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"state\":\"ON\"}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":2,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:18:08+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":223,\"power_on_behavior\":\"off\",\"state\":\"ON\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Aqara switch T1","payload":"{\"consumption\":0.000018083439499605447,\"current\":0.03,\"device_temperature\":23,\"energy\":0,\"last_seen\":\"2024-11-23T15:18:09+01:00\",\"led_disabled_night\":false,\"linkquality\":223,\"power\":5.45,\"power_outage_count\":1,\"power_outage_memory\":false,\"state\":\"ON\",\"switch_type\":\"toggle\",\"voltage\":234.76}"} +{"entity":"EARU Circuit Breaker 16A","payload":"{\"current\":0,\"energy\":0.4,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:18:10+01:00\",\"linkquality\":229,\"power\":0,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":29.96,\"voltage\":234}"} +{"entity":"EARU Circuit Breaker 16A","payload":"{\"current\":0,\"energy\":0.4,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:18:10+01:00\",\"linkquality\":229,\"power\":0,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":30.03,\"voltage\":234}"} +{"entity":"EARU Circuit Breaker 16A","payload":"{\"current\":0,\"energy\":0.4,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:18:10+01:00\",\"linkquality\":229,\"power\":0,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":30.03,\"voltage\":234}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":360,\"formaldehyd\":1,\"humidity\":34,\"last_seen\":\"2024-11-23T15:18:11+01:00\",\"linkquality\":103,\"temperature\":21.9,\"voc\":2}"} +{"entity":"Moes dimmer double","payload":"{\"brightness_l1\":254,\"brightness_l2\":254,\"last_seen\":\"2024-11-23T15:17:22+01:00\",\"linkquality\":108,\"state_l1\":\"ON\",\"state_l2\":\"OFF\"}"} +{"entity":"Moes dimmer double","payload":"{\"brightness_l1\":254,\"brightness_l2\":254,\"last_seen\":\"2024-11-23T15:18:13+01:00\",\"linkquality\":163,\"state_l1\":\"ON\",\"state_l2\":\"OFF\"}"} +{"entity":"Moes dimmer double","payload":"{\"brightness_l1\":254,\"brightness_l2\":254,\"last_seen\":\"2024-11-23T15:18:14+01:00\",\"linkquality\":163,\"state_l1\":\"ON\",\"state_l2\":\"OFF\"}"} +{"entity":"Moes RGBCCT light bulb","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"last_seen\":\"2024-11-23T15:18:14+01:00\",\"linkquality\":130,\"state\":\"OFF\"}"} +{"entity":"Lights","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"state\":\"ON\"}"} +{"entity":"Moes RGBCCT light bulb","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"last_seen\":\"2024-11-23T15:18:14+01:00\",\"linkquality\":130,\"state\":\"ON\"}"} +{"entity":"Moes RGBCCT light bulb II","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:18:16+01:00\",\"linkquality\":229,\"state\":\"OFF\"}"} +{"entity":"Moes RGBCCT light bulb II","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:18:16+01:00\",\"linkquality\":229,\"state\":\"ON\"}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":2,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:18:16+01:00\",\"linkquality\":180,\"state\":\"ON\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":2,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:18:16+01:00\",\"linkquality\":184,\"state\":\"ON\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Moes RGBCCT light bulb III","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:18:17+01:00\",\"linkquality\":217,\"state\":\"OFF\"}"} +{"entity":"Moes RGBCCT light bulb III","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:18:17+01:00\",\"linkquality\":217,\"state\":\"ON\"}"} +{"entity":"Moes RGBCCT light bulb III","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:18:18+01:00\",\"linkquality\":217,\"state\":\"ON\"}"} +{"entity":"Lights","payload":"{\"brightness\":254,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"state\":\"ON\"}"} +{"entity":"Moes RGBCCT light bulb III","payload":"{\"brightness\":254,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:18:18+01:00\",\"linkquality\":217,\"state\":\"ON\"}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:18:19+01:00\",\"linkquality\":250,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.86,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Moes RGBCCT light bulb II","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:18:19+01:00\",\"linkquality\":229,\"state\":\"ON\"}"} +{"entity":"Moes RGBCCT light bulb II","payload":"{\"brightness\":254,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:18:19+01:00\",\"linkquality\":229,\"state\":\"ON\"}"} +{"entity":"EARU Circuit Breaker 32A","payload":"{\"current\":0.1,\"energy\":4.26,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:18:20+01:00\",\"linkquality\":207,\"power\":16,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":31.96,\"voltage\":234}"} +{"entity":"Moes RGBCCT light bulb","payload":"{\"brightness\":1,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"last_seen\":\"2024-11-23T15:18:21+01:00\",\"linkquality\":124,\"state\":\"ON\"}"} +{"entity":"Moes RGBCCT light bulb","payload":"{\"brightness\":254,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"last_seen\":\"2024-11-23T15:18:21+01:00\",\"linkquality\":124,\"state\":\"ON\"}"} +{"entity":"EARU Circuit Breaker 16A","payload":"{\"current\":0,\"energy\":0.4,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:18:10+01:00\",\"linkquality\":229,\"power\":0,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":30.03,\"voltage\":234}"} +{"entity":"EARU Circuit Breaker 16A","payload":"{\"current\":0,\"energy\":0.4,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:18:10+01:00\",\"linkquality\":229,\"power\":0,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":30.03,\"voltage\":234}"} +{"entity":"EARU Circuit Breaker 32A","payload":"{\"current\":0.1,\"energy\":4.26,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:18:22+01:00\",\"linkquality\":207,\"power\":26,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":31.96,\"voltage\":234}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:18:22+01:00\",\"linkquality\":250,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":9.32,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":360,\"formaldehyd\":3,\"humidity\":34.2,\"last_seen\":\"2024-11-23T15:18:21+01:00\",\"linkquality\":91,\"temperature\":21.9,\"voc\":3}"} +{"entity":"Moes switch double","payload":"{\"last_seen\":\"2024-11-23T15:18:24+01:00\",\"linkquality\":240,\"power_on_behavior\":\"off\",\"power_on_behavior_l1\":\"off\",\"state_l1\":\"ON\",\"state_l2\":\"OFF\"}"} +{"entity":"Moes switch double","payload":"{\"last_seen\":\"2024-11-23T15:18:24+01:00\",\"linkquality\":234,\"power_on_behavior\":\"off\",\"power_on_behavior_l1\":\"off\",\"state_l1\":\"ON\",\"state_l2\":\"OFF\"}"} +{"entity":"Moes switch double","payload":"{\"last_seen\":\"2024-11-23T15:18:24+01:00\",\"linkquality\":234,\"power_on_behavior\":\"off\",\"power_on_behavior_l1\":\"off\",\"state_l1\":\"ON\",\"state_l2\":\"OFF\"}"} +{"entity":"Develco air quality","payload":"{\"air_quality\":\"moderate\",\"battery\":20,\"humidity\":30,\"last_seen\":\"2024-11-23T15:18:25+01:00\",\"linkquality\":255,\"temperature\":22.7,\"update\":{\"installed_version\":262145,\"latest_version\":262145,\"state\":\"idle\"},\"voc\":1080,\"voltage\":2600}"} +{"entity":"Moes switch double","payload":"{\"last_seen\":\"2024-11-23T15:18:26+01:00\",\"linkquality\":240,\"power_on_behavior\":\"off\",\"power_on_behavior_l1\":\"off\",\"state_l1\":\"ON\",\"state_l2\":\"OFF\"}"} +{"entity":"Moes switch double","payload":"{\"last_seen\":\"2024-11-23T15:18:26+01:00\",\"linkquality\":240,\"power_on_behavior\":\"off\",\"power_on_behavior_l1\":\"off\",\"state_l1\":\"ON\",\"state_l2\":\"ON\"}"} +{"entity":"Moes switch double","payload":"{\"last_seen\":\"2024-11-23T15:18:26+01:00\",\"linkquality\":240,\"power_on_behavior\":\"off\",\"power_on_behavior_l1\":\"off\",\"state_l1\":\"ON\",\"state_l2\":\"ON\"}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":362,\"formaldehyd\":1,\"humidity\":33.8,\"last_seen\":\"2024-11-23T15:18:28+01:00\",\"linkquality\":91,\"temperature\":21.8,\"voc\":3}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":359,\"formaldehyd\":4,\"humidity\":34.1,\"last_seen\":\"2024-11-23T15:18:31+01:00\",\"linkquality\":91,\"temperature\":21.9,\"voc\":4}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:18:35+01:00\",\"linkquality\":240,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":9.32,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:18:37+01:00\",\"linkquality\":124,\"motor_reversal\":\"OFF\",\"moving\":\"STOP\",\"position\":0,\"state\":\"CLOSE\"}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:18:37+01:00\",\"linkquality\":124,\"motor_reversal\":\"OFF\",\"moving\":\"UP\",\"position\":0,\"state\":\"CLOSE\"}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:18:38+01:00\",\"linkquality\":124,\"motor_reversal\":\"OFF\",\"moving\":\"STOP\",\"position\":0,\"state\":\"CLOSE\"}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:18:38+01:00\",\"linkquality\":130,\"motor_reversal\":\"OFF\",\"moving\":\"STOP\",\"position\":0,\"state\":\"CLOSE\"}"} +{"entity":"Occupancy sensor P1","payload":"{\"battery\":100,\"detection_interval\":30,\"device_temperature\":25,\"illuminance\":558,\"last_seen\":\"2024-11-23T15:18:38+01:00\",\"linkquality\":255,\"motion_sensitivity\":\"medium\",\"occupancy\":true,\"power_outage_count\":34,\"trigger_indicator\":false,\"voltage\":3032}"} +{"entity":"Occupancy sensor","payload":"{\"battery\":100,\"device_temperature\":29,\"illuminance\":244,\"illuminance_lux\":244,\"last_seen\":\"2024-11-23T15:18:39+01:00\",\"linkquality\":91,\"occupancy\":true,\"power_outage_count\":548,\"voltage\":3035}"} +{"entity":"Occupancy sensor","payload":"{\"battery\":100,\"device_temperature\":29,\"illuminance\":244,\"illuminance_lux\":244,\"last_seen\":\"2024-11-23T15:18:39+01:00\",\"linkquality\":91,\"occupancy\":true,\"power_outage_count\":548,\"voltage\":3035}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:18:39+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:18:39+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:18:39+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":362,\"formaldehyd\":1,\"humidity\":34.1,\"last_seen\":\"2024-11-23T15:18:38+01:00\",\"linkquality\":81,\"temperature\":22.1,\"voc\":3}"} +{"entity":"Mini luminance motion sensor","payload":"{\"battery\":100,\"illuminance\":2545,\"keep_time\":\"60\",\"last_seen\":\"2024-11-23T15:18:43+01:00\",\"linkquality\":91,\"occupancy\":true,\"sensitivity\":\"high\"}"} +{"entity":"Mini luminance motion sensor","payload":"{\"battery\":100,\"illuminance\":2545,\"keep_time\":\"60\",\"last_seen\":\"2024-11-23T15:18:43+01:00\",\"linkquality\":60,\"occupancy\":true,\"sensitivity\":\"high\"}"} +{"entity":"Mini luminance motion sensor","payload":"{\"battery\":100,\"illuminance\":2545,\"keep_time\":\"60\",\"last_seen\":\"2024-11-23T15:18:44+01:00\",\"linkquality\":97,\"occupancy\":true,\"sensitivity\":\"high\"}"} +{"entity":"Mini luminance motion sensor","payload":"{\"battery\":100,\"illuminance\":2493,\"keep_time\":\"60\",\"last_seen\":\"2024-11-23T15:18:44+01:00\",\"linkquality\":87,\"occupancy\":true,\"sensitivity\":\"high\"}"} +{"entity":"Vibration sensor","payload":"{\"action\":\"vibration\",\"angle\":9,\"angle_x\":2,\"angle_x_absolute\":88,\"angle_y\":2,\"angle_y_absolute\":88,\"angle_z\":87,\"battery\":100,\"device_temperature\":25,\"last_seen\":\"2024-11-23T15:18:44+01:00\",\"linkquality\":240,\"power_outage_count\":7,\"strength\":2,\"vibration\":true,\"voltage\":3005,\"x_axis\":48,\"y_axis\":36,\"z_axis\":1311}"} +{"entity":"Mini luminance motion sensor","payload":"{\"battery\":100,\"illuminance\":2493,\"keep_time\":\"60\",\"last_seen\":\"2024-11-23T15:18:44+01:00\",\"linkquality\":70,\"occupancy\":true,\"sensitivity\":\"high\"}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":2,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:18:46+01:00\",\"linkquality\":168,\"state\":\"ON\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":2,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:18:46+01:00\",\"linkquality\":201,\"state\":\"ON\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":363,\"formaldehyd\":4,\"humidity\":34.1,\"last_seen\":\"2024-11-23T15:18:46+01:00\",\"linkquality\":91,\"temperature\":21.9,\"voc\":2}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:18:48+01:00\",\"linkquality\":234,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":9.32,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Vibration sensor","payload":"{\"angle\":9,\"angle_x\":2,\"angle_x_absolute\":88,\"angle_y\":2,\"angle_y_absolute\":88,\"angle_z\":87,\"battery\":100,\"device_temperature\":25,\"last_seen\":\"2024-11-23T15:18:48+01:00\",\"linkquality\":207,\"power_outage_count\":7,\"strength\":2,\"vibration\":true,\"voltage\":3005,\"x_axis\":48,\"y_axis\":36,\"z_axis\":1311}"} +{"entity":"Contact sensor","payload":"{\"battery\":77,\"contact\":false,\"device_temperature\":33,\"last_seen\":\"2024-11-23T15:18:50+01:00\",\"linkquality\":255,\"power_outage_count\":50,\"trigger_count\":0,\"voltage\":2965}"} +{"entity":"Contact sensor","payload":"{\"battery\":77,\"contact\":true,\"device_temperature\":33,\"last_seen\":\"2024-11-23T15:18:50+01:00\",\"linkquality\":255,\"power_outage_count\":50,\"trigger_count\":0,\"voltage\":2965}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":363,\"formaldehyd\":3,\"humidity\":34,\"last_seen\":\"2024-11-23T15:18:49+01:00\",\"linkquality\":108,\"temperature\":22,\"voc\":3}"} +{"entity":"EARU Circuit Breaker 16A","payload":"{\"current\":0,\"energy\":0.4,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:18:51+01:00\",\"linkquality\":229,\"power\":0,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":30.03,\"voltage\":234}"} +{"entity":"Climate sensor","payload":"{\"battery\":37,\"humidity\":31.32,\"last_seen\":\"2024-11-23T15:18:54+01:00\",\"linkquality\":255,\"power_outage_count\":77,\"pressure\":1005.2,\"temperature\":22.51,\"voltage\":2905}"} +{"entity":"Climate sensor","payload":"{\"battery\":37,\"humidity\":32.8,\"last_seen\":\"2024-11-23T15:18:54+01:00\",\"linkquality\":255,\"power_outage_count\":77,\"pressure\":1005.1,\"temperature\":22.51,\"voltage\":2905}"} +{"entity":"Light sensor","payload":"{\"battery\":100,\"device_temperature\":25,\"illuminance\":22719,\"illuminance_lux\":187,\"last_seen\":\"2024-11-23T15:18:55+01:00\",\"linkquality\":255,\"power_outage_count\":34,\"state\":\"OFF\",\"voltage\":3068}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":363,\"formaldehyd\":4,\"humidity\":33.6,\"last_seen\":\"2024-11-23T15:18:53+01:00\",\"linkquality\":108,\"temperature\":21.9,\"voc\":1}"} +{"entity":"Climate sensor","payload":"{\"battery\":37,\"humidity\":32.8,\"last_seen\":\"2024-11-23T15:18:56+01:00\",\"linkquality\":255,\"power_outage_count\":77,\"pressure\":1005.1,\"temperature\":22.51,\"voltage\":2905}"} +{"entity":"Climate sensor","payload":"{\"battery\":37,\"humidity\":32.8,\"last_seen\":\"2024-11-23T15:18:56+01:00\",\"linkquality\":255,\"power_outage_count\":77,\"pressure\":1005.1,\"temperature\":22.51,\"voltage\":2905}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:18:54+01:00\",\"linkquality\":207,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":361,\"formaldehyd\":1,\"humidity\":33.7,\"last_seen\":\"2024-11-23T15:18:56+01:00\",\"linkquality\":87,\"temperature\":22,\"voc\":1}"} +{"entity":"Light sensor","payload":"{\"battery\":100,\"device_temperature\":25,\"illuminance\":24457,\"illuminance_lux\":279,\"last_seen\":\"2024-11-23T15:19:00+01:00\",\"linkquality\":255,\"power_outage_count\":34,\"state\":\"OFF\",\"voltage\":3068}"} +{"entity":"EARU Circuit Breaker 32A","payload":"{\"current\":0.1,\"energy\":4.26,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:19:01+01:00\",\"linkquality\":223,\"power\":26,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":31.96,\"voltage\":234}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:19:01+01:00\",\"linkquality\":217,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":9.32,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Light sensor","payload":"{\"battery\":100,\"device_temperature\":25,\"illuminance\":24457,\"illuminance_lux\":279,\"last_seen\":\"2024-11-23T15:19:02+01:00\",\"linkquality\":229,\"power_outage_count\":34,\"state\":\"OFF\",\"voltage\":3068}"} +{"entity":"Light sensor","payload":"{\"battery\":100,\"device_temperature\":25,\"illuminance\":24457,\"illuminance_lux\":279,\"last_seen\":\"2024-11-23T15:19:02+01:00\",\"linkquality\":234,\"power_outage_count\":34,\"state\":\"OFF\",\"voltage\":3068}"} +{"entity":"Smart air box 2 (_TZE200_mja3fuja)","payload":"{\"co2\":368,\"formaldehyd\":2,\"humidity\":31,\"last_seen\":\"2024-11-23T15:19:03+01:00\",\"linkquality\":255,\"temperature\":23.3,\"voc\":1}"} +{"entity":"Light sensor","payload":"{\"battery\":100,\"device_temperature\":25,\"illuminance\":23444,\"illuminance_lux\":221,\"last_seen\":\"2024-11-23T15:19:05+01:00\",\"linkquality\":255,\"power_outage_count\":34,\"state\":\"OFF\",\"voltage\":3068}"} +{"entity":"Scenes button","payload":"{\"action\":\"single\",\"battery\":100,\"device_temperature\":29,\"last_seen\":\"2024-11-23T15:19:05+01:00\",\"linkquality\":114,\"power_outage_count\":34,\"voltage\":3055}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":364,\"formaldehyd\":3,\"humidity\":33.8,\"last_seen\":\"2024-11-23T15:19:04+01:00\",\"linkquality\":124,\"temperature\":22.1,\"voc\":1}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:19:06+01:00\",\"linkquality\":250,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":9.32,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Climate sensor","payload":"{\"battery\":37,\"humidity\":32.8,\"last_seen\":\"2024-11-23T15:19:07+01:00\",\"linkquality\":255,\"power_outage_count\":77,\"pressure\":1005.1,\"temperature\":22.66,\"voltage\":2905}"} +{"entity":"Climate sensor","payload":"{\"battery\":37,\"humidity\":38.41,\"last_seen\":\"2024-11-23T15:19:07+01:00\",\"linkquality\":255,\"power_outage_count\":77,\"pressure\":1005.1,\"temperature\":22.66,\"voltage\":2905}"} +{"entity":"Climate sensor","payload":"{\"battery\":37,\"humidity\":38.41,\"last_seen\":\"2024-11-23T15:19:07+01:00\",\"linkquality\":255,\"power_outage_count\":77,\"pressure\":1005,\"temperature\":22.66,\"voltage\":2905}"} +{"entity":"Scenes button","payload":"{\"action\":\"double\",\"battery\":100,\"device_temperature\":29,\"last_seen\":\"2024-11-23T15:19:07+01:00\",\"linkquality\":120,\"power_outage_count\":34,\"voltage\":3055}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":365,\"formaldehyd\":2,\"humidity\":33.7,\"last_seen\":\"2024-11-23T15:19:06+01:00\",\"linkquality\":114,\"temperature\":21.8,\"voc\":1}"} +{"entity":"Scenes button","payload":"{\"action\":\"hold\",\"battery\":100,\"device_temperature\":29,\"last_seen\":\"2024-11-23T15:19:09+01:00\",\"linkquality\":120,\"power_outage_count\":34,\"voltage\":3055}"} +{"entity":"Occupancy sensor P1","payload":"{\"battery\":100,\"detection_interval\":30,\"device_temperature\":25,\"illuminance\":540,\"last_seen\":\"2024-11-23T15:19:10+01:00\",\"linkquality\":255,\"motion_sensitivity\":\"medium\",\"occupancy\":true,\"power_outage_count\":34,\"trigger_indicator\":false,\"voltage\":3032}"} +{"entity":"Scenes button","payload":"{\"action\":\"release\",\"battery\":100,\"device_temperature\":29,\"last_seen\":\"2024-11-23T15:19:11+01:00\",\"linkquality\":91,\"power_outage_count\":34,\"voltage\":3055}"} +{"entity":"Light sensor","payload":"{\"battery\":100,\"device_temperature\":25,\"illuminance\":24594,\"illuminance_lux\":288,\"last_seen\":\"2024-11-23T15:19:11+01:00\",\"linkquality\":255,\"power_outage_count\":34,\"state\":\"OFF\",\"voltage\":3068}"} +{"entity":"Leak sensor","payload":"{\"battery\":100,\"device_temperature\":23,\"last_seen\":\"2024-11-23T15:19:14+01:00\",\"linkquality\":124,\"power_outage_count\":13,\"trigger_count\":-1,\"voltage\":3045}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":364,\"formaldehyd\":4,\"humidity\":34,\"last_seen\":\"2024-11-23T15:19:14+01:00\",\"linkquality\":114,\"temperature\":21.9,\"voc\":5}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":2,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:19:16+01:00\",\"linkquality\":184,\"state\":\"ON\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":2,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:19:16+01:00\",\"linkquality\":217,\"state\":\"ON\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Smart air box 2 (_TZE200_mja3fuja)","payload":"{\"co2\":358,\"formaldehyd\":1,\"humidity\":32,\"last_seen\":\"2024-11-23T15:19:17+01:00\",\"linkquality\":255,\"temperature\":23.3,\"voc\":0}"} +{"entity":"Smart button","payload":"{\"battery\":98,\"last_seen\":\"2024-11-23T15:19:18+01:00\",\"linkquality\":70,\"voltage\":2900}"} +{"entity":"Smart button","payload":"{\"action\":\"brightness_move_to_level\",\"action_level\":63,\"action_transition_time\":0.01,\"battery\":98,\"last_seen\":\"2024-11-23T15:19:18+01:00\",\"linkquality\":70,\"voltage\":2900}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:19:19+01:00\",\"linkquality\":234,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":9.32,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":366,\"formaldehyd\":1,\"humidity\":34.2,\"last_seen\":\"2024-11-23T15:19:21+01:00\",\"linkquality\":60,\"temperature\":22,\"voc\":1}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":367,\"formaldehyd\":1,\"humidity\":33.7,\"last_seen\":\"2024-11-23T15:19:24+01:00\",\"linkquality\":97,\"temperature\":22.1,\"voc\":4}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:19:32+01:00\",\"linkquality\":244,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":9.32,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":367,\"formaldehyd\":2,\"humidity\":34.3,\"last_seen\":\"2024-11-23T15:19:31+01:00\",\"linkquality\":70,\"temperature\":21.8,\"voc\":2}"} +{"entity":"Aqara switch no neutral","payload":"{\"device_temperature\":30,\"last_seen\":\"2024-11-23T15:19:34+01:00\",\"linkquality\":255,\"power_outage_count\":0,\"power_outage_memory\":false,\"state\":\"ON\",\"switch_type\":\"toggle\"}"} +{"entity":"Aqara switch no neutral","payload":"{\"device_temperature\":30,\"last_seen\":\"2024-11-23T15:19:34+01:00\",\"linkquality\":255,\"power_outage_count\":0,\"power_outage_memory\":false,\"state\":\"OFF\",\"switch_type\":\"toggle\"}"} +{"entity":"Aqara switch no neutral","payload":"{\"device_temperature\":30,\"last_seen\":\"2024-11-23T15:19:34+01:00\",\"linkquality\":255,\"power_outage_count\":0,\"power_outage_memory\":false,\"state\":\"OFF\",\"switch_type\":\"toggle\"}"} +{"entity":"Aqara switch T1","payload":"{\"consumption\":0.000018083439499605447,\"current\":0.03,\"device_temperature\":23,\"energy\":0,\"last_seen\":\"2024-11-23T15:19:35+01:00\",\"led_disabled_night\":false,\"linkquality\":229,\"power\":5.45,\"power_outage_count\":1,\"power_outage_memory\":false,\"state\":\"ON\",\"switch_type\":\"toggle\",\"voltage\":234.76}"} +{"entity":"Aqara switch T1","payload":"{\"consumption\":0.000018083439499605447,\"current\":0.03,\"device_temperature\":23,\"energy\":0,\"last_seen\":\"2024-11-23T15:19:35+01:00\",\"led_disabled_night\":false,\"linkquality\":234,\"power\":5.45,\"power_outage_count\":1,\"power_outage_memory\":false,\"state\":\"OFF\",\"switch_type\":\"toggle\",\"voltage\":234.76}"} +{"entity":"Aqara switch T1","payload":"{\"consumption\":0.000018083439499605447,\"current\":0.03,\"device_temperature\":23,\"energy\":0,\"last_seen\":\"2024-11-23T15:19:35+01:00\",\"led_disabled_night\":false,\"linkquality\":234,\"power\":5.45,\"power_outage_count\":1,\"power_outage_memory\":false,\"state\":\"OFF\",\"switch_type\":\"toggle\",\"voltage\":234.76}"} +{"entity":"Aqara switch T1","payload":"{\"consumption\":0.000018083439499605447,\"current\":0.03,\"device_temperature\":23,\"energy\":0,\"last_seen\":\"2024-11-23T15:19:36+01:00\",\"led_disabled_night\":false,\"linkquality\":234,\"power\":0,\"power_outage_count\":1,\"power_outage_memory\":false,\"state\":\"OFF\",\"switch_type\":\"toggle\",\"voltage\":234.76}"} +{"entity":"Occupancy sensor","payload":"{\"battery\":100,\"device_temperature\":29,\"illuminance\":244,\"illuminance_lux\":244,\"last_seen\":\"2024-11-23T15:18:39+01:00\",\"linkquality\":91,\"occupancy\":false,\"power_outage_count\":548,\"voltage\":3035}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:19:39+01:00\",\"linkquality\":213,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:19:39+01:00\",\"linkquality\":213,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:19:39+01:00\",\"linkquality\":213,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Aqara switch no neutral","payload":"{\"device_temperature\":30,\"last_seen\":\"2024-11-23T15:19:40+01:00\",\"linkquality\":255,\"power_outage_count\":0,\"power_outage_memory\":false,\"state\":\"OFF\",\"switch_type\":\"toggle\"}"} +{"entity":"Eglo RGBCTT light","payload":"{\"brightness\":1,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":370,\"last_seen\":\"2024-11-23T15:19:40+01:00\",\"linkquality\":240,\"power_on_behavior\":\"off\",\"state\":\"ON\"}"} +{"entity":"Eglo RGBCTT light","payload":"{\"brightness\":254,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":370,\"last_seen\":\"2024-11-23T15:19:40+01:00\",\"linkquality\":240,\"power_on_behavior\":\"off\",\"state\":\"ON\"}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":367,\"formaldehyd\":4,\"humidity\":34.4,\"last_seen\":\"2024-11-23T15:19:39+01:00\",\"linkquality\":87,\"temperature\":21.9,\"voc\":3}"} +{"entity":"Eglo RGBCTT light","payload":"{\"brightness\":254,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":370,\"last_seen\":\"2024-11-23T15:19:41+01:00\",\"linkquality\":240,\"power_on_behavior\":\"off\",\"state\":\"ON\"}"} +{"entity":"Eglo RGBCTT light","payload":"{\"brightness\":254,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":370,\"last_seen\":\"2024-11-23T15:19:41+01:00\",\"linkquality\":240,\"power_on_behavior\":\"off\",\"state\":\"OFF\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:18:03+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":368,\"formaldehyd\":1,\"humidity\":34.3,\"last_seen\":\"2024-11-23T15:19:42+01:00\",\"linkquality\":64,\"temperature\":21.8,\"voc\":2}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:44+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:44+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:44+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:44+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:44+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:44+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:44+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:44+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:44+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:44+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:44+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:44+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:44+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:44+01:00\",\"linkquality\":255,\"state\":\"ON\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:44+01:00\",\"linkquality\":255,\"state\":\"OFF\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":255,\"state\":\"OFF\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":255,\"state\":\"OFF\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":255,\"state\":\"OFF\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":255,\"state\":\"OFF\"}"} +{"entity":"Smart air box 2 (_TZE200_mja3fuja)","payload":"{\"co2\":358,\"formaldehyd\":1,\"humidity\":32,\"last_seen\":\"2024-11-23T15:19:45+01:00\",\"linkquality\":255,\"temperature\":23.3,\"voc\":0}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":255,\"state\":\"OFF\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":255,\"state\":\"OFF\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":255,\"state\":\"OFF\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":255,\"state\":\"OFF\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":255,\"state\":\"OFF\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":255,\"state\":\"OFF\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":255,\"state\":\"OFF\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":255,\"state\":\"OFF\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":255,\"state\":\"OFF\"}"} +{"entity":"Gledopto GL-SD-001","payload":"{\"brightness\":254,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":255,\"state\":\"OFF\"}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":240,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":9.32,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":2,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":163,\"state\":\"ON\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":2,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":207,\"state\":\"ON\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:19:46+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":2,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:19:47+01:00\",\"linkquality\":207,\"state\":\"ON\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":254,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:19:47+01:00\",\"linkquality\":207,\"state\":\"ON\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":254,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:19:48+01:00\",\"linkquality\":201,\"state\":\"ON\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":254,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:19:48+01:00\",\"linkquality\":201,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":254,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:19:48+01:00\",\"linkquality\":207,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":2,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:19:50+01:00\",\"linkquality\":157,\"state\":\"ON\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":254,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:19:50+01:00\",\"linkquality\":157,\"state\":\"ON\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":366,\"formaldehyd\":4,\"humidity\":34.3,\"last_seen\":\"2024-11-23T15:19:49+01:00\",\"linkquality\":54,\"temperature\":21.8,\"voc\":5}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":254,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:19:51+01:00\",\"linkquality\":157,\"state\":\"ON\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":254,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:19:51+01:00\",\"linkquality\":157,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":254,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:19:52+01:00\",\"linkquality\":163,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":358,\"formaldehyd\":3,\"humidity\":34.2,\"last_seen\":\"2024-11-23T15:19:54+01:00\",\"linkquality\":87,\"temperature\":22.1,\"voc\":4}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":2,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:19:54+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":213,\"power_on_behavior\":\"off\",\"state\":\"ON\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":254,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:19:54+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":213,\"power_on_behavior\":\"off\",\"state\":\"ON\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":358,\"formaldehyd\":3,\"humidity\":34.2,\"last_seen\":\"2024-11-23T15:19:54+01:00\",\"linkquality\":91,\"temperature\":22.1,\"voc\":4}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:19:54+01:00\",\"linkquality\":207,\"local_temperature\":28.5,\"max_temperature_limit\":30,\"min_temperature_limit\":5,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":254,\"color\":{\"hue\":32,\"saturation\":82,\"x\":0.4599,\"y\":0.4106},\"color_mode\":\"color_temp\",\"color_temp\":370,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:19:57+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":201,\"power_on_behavior\":\"off\",\"state\":\"ON\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Lights","payload":"{\"brightness\":254,\"color\":{\"hue\":22,\"saturation\":97,\"x\":0.5492,\"y\":0.4082},\"color_mode\":\"color_temp\",\"color_temp\":555,\"state\":\"ON\"}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":254,\"color\":{\"hue\":22,\"saturation\":97,\"x\":0.5492,\"y\":0.4082},\"color_mode\":\"color_temp\",\"color_temp\":555,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:19:57+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":201,\"power_on_behavior\":\"off\",\"state\":\"ON\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":254,\"color\":{\"hue\":22,\"saturation\":97,\"x\":0.5492,\"y\":0.4082},\"color_mode\":\"color_temp\",\"color_temp\":555,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:19:58+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":207,\"power_on_behavior\":\"off\",\"state\":\"ON\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Inrr RGBCTT light","payload":"{\"brightness\":254,\"color\":{\"hue\":22,\"saturation\":97,\"x\":0.5492,\"y\":0.4082},\"color_mode\":\"color_temp\",\"color_temp\":555,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:19:58+01:00\",\"level_config\":{\"on_level\":\"previous\"},\"linkquality\":207,\"power_on_behavior\":\"off\",\"state\":\"OFF\",\"update\":{\"installed_version\":537160240,\"latest_version\":537160240,\"state\":\"idle\"}}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":356,\"formaldehyd\":3,\"humidity\":33.7,\"last_seen\":\"2024-11-23T15:19:57+01:00\",\"linkquality\":76,\"temperature\":21.9,\"voc\":5}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":359,\"formaldehyd\":1,\"humidity\":34.2,\"last_seen\":\"2024-11-23T15:19:59+01:00\",\"linkquality\":76,\"temperature\":22.1,\"voc\":2}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:20:04+01:00\",\"linkquality\":240,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":9.32,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Moes dimmer double","payload":"{\"brightness_l1\":254,\"brightness_l2\":254,\"last_seen\":\"2024-11-23T15:18:14+01:00\",\"linkquality\":163,\"state_l1\":\"ON\",\"state_l2\":\"ON\"}"} +{"entity":"Moes dimmer double","payload":"{\"brightness_l1\":254,\"brightness_l2\":254,\"last_seen\":\"2024-11-23T15:20:06+01:00\",\"linkquality\":141,\"state_l1\":\"ON\",\"state_l2\":\"ON\"}"} +{"entity":"Moes dimmer double","payload":"{\"brightness_l1\":254,\"brightness_l2\":254,\"last_seen\":\"2024-11-23T15:20:07+01:00\",\"linkquality\":141,\"state_l1\":\"ON\",\"state_l2\":\"ON\"}"} +{"entity":"Moes dimmer double","payload":"{\"brightness_l1\":254,\"brightness_l2\":254,\"last_seen\":\"2024-11-23T15:20:07+01:00\",\"linkquality\":141,\"state_l1\":\"ON\",\"state_l2\":\"OFF\"}"} +{"entity":"Moes dimmer double","payload":"{\"brightness_l1\":254,\"brightness_l2\":254,\"last_seen\":\"2024-11-23T15:20:08+01:00\",\"linkquality\":141,\"state_l1\":\"ON\",\"state_l2\":\"OFF\"}"} +{"entity":"Moes dimmer double","payload":"{\"brightness_l1\":254,\"brightness_l2\":254,\"last_seen\":\"2024-11-23T15:20:08+01:00\",\"linkquality\":136,\"state_l1\":\"ON\",\"state_l2\":\"OFF\"}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":357,\"formaldehyd\":2,\"humidity\":33.9,\"last_seen\":\"2024-11-23T15:20:07+01:00\",\"linkquality\":54,\"temperature\":21.9,\"voc\":5}"} +{"entity":"Moes dimmer double","payload":"{\"brightness_l1\":254,\"brightness_l2\":254,\"last_seen\":\"2024-11-23T15:20:08+01:00\",\"linkquality\":136,\"state_l1\":\"OFF\",\"state_l2\":\"OFF\"}"} +{"entity":"Moes dimmer double","payload":"{\"brightness_l1\":254,\"brightness_l2\":254,\"last_seen\":\"2024-11-23T15:20:09+01:00\",\"linkquality\":141,\"state_l1\":\"OFF\",\"state_l2\":\"OFF\"}"} +{"entity":"Moes dimmer double","payload":"{\"brightness_l1\":254,\"brightness_l2\":254,\"last_seen\":\"2024-11-23T15:20:09+01:00\",\"linkquality\":141,\"state_l1\":\"OFF\",\"state_l2\":\"OFF\"}"} +{"entity":"Occupancy sensor P1","payload":"{\"battery\":100,\"detection_interval\":30,\"device_temperature\":25,\"illuminance\":540,\"last_seen\":\"2024-11-23T15:19:10+01:00\",\"linkquality\":255,\"motion_sensitivity\":\"medium\",\"occupancy\":false,\"power_outage_count\":34,\"trigger_indicator\":false,\"voltage\":3032}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":356,\"formaldehyd\":2,\"humidity\":33.9,\"last_seen\":\"2024-11-23T15:20:10+01:00\",\"linkquality\":87,\"temperature\":21.9,\"voc\":4}"} +{"entity":"Smart air box 2 (_TZE200_mja3fuja)","payload":"{\"co2\":368,\"formaldehyd\":2,\"humidity\":32,\"last_seen\":\"2024-11-23T15:20:12+01:00\",\"linkquality\":255,\"temperature\":23.3,\"voc\":1}"} +{"entity":"Moes dimmer double","payload":"{\"brightness_l1\":254,\"brightness_l2\":254,\"last_seen\":\"2024-11-23T15:20:13+01:00\",\"linkquality\":136,\"state_l1\":\"OFF\",\"state_l2\":\"OFF\"}"} +{"entity":"Moes RGBCCT light bulb","payload":"{\"brightness\":254,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"last_seen\":\"2024-11-23T15:20:13+01:00\",\"linkquality\":157,\"state\":\"ON\"}"} +{"entity":"Moes RGBCCT light bulb","payload":"{\"brightness\":254,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"last_seen\":\"2024-11-23T15:20:13+01:00\",\"linkquality\":157,\"state\":\"OFF\"}"} +{"entity":"Moes dimmer double","payload":"{\"brightness_l1\":254,\"brightness_l2\":254,\"last_seen\":\"2024-11-23T15:20:13+01:00\",\"linkquality\":141,\"state_l1\":\"OFF\",\"state_l2\":\"OFF\"}"} +{"entity":"Moes dimmer double","payload":"{\"brightness_l1\":254,\"brightness_l2\":254,\"last_seen\":\"2024-11-23T15:20:14+01:00\",\"linkquality\":136,\"state_l1\":\"OFF\",\"state_l2\":\"OFF\"}"} +{"entity":"Moes dimmer double","payload":"{\"brightness_l1\":254,\"brightness_l2\":254,\"last_seen\":\"2024-11-23T15:20:14+01:00\",\"linkquality\":141,\"state_l1\":\"OFF\",\"state_l2\":\"OFF\"}"} +{"entity":"Vibration sensor","payload":"{\"angle\":9,\"angle_x\":2,\"angle_x_absolute\":88,\"angle_y\":2,\"angle_y_absolute\":88,\"angle_z\":87,\"battery\":100,\"device_temperature\":25,\"last_seen\":\"2024-11-23T15:18:48+01:00\",\"linkquality\":207,\"power_outage_count\":7,\"strength\":2,\"vibration\":false,\"voltage\":3005,\"x_axis\":48,\"y_axis\":36,\"z_axis\":1311}"} +{"entity":"Moes RGBCCT light bulb II","payload":"{\"brightness\":254,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:20:14+01:00\",\"linkquality\":223,\"state\":\"ON\"}"} +{"entity":"Moes RGBCCT light bulb II","payload":"{\"brightness\":254,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:20:14+01:00\",\"linkquality\":223,\"state\":\"OFF\"}"} +{"entity":"Moes RGBCCT light bulb III","payload":"{\"brightness\":254,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:20:15+01:00\",\"linkquality\":217,\"state\":\"ON\"}"} +{"entity":"Lights","payload":"{\"brightness\":254,\"color\":{\"hue\":22,\"saturation\":97,\"x\":0.5492,\"y\":0.4082},\"color_mode\":\"color_temp\",\"color_temp\":555,\"state\":\"OFF\"}"} +{"entity":"Moes RGBCCT light bulb III","payload":"{\"brightness\":254,\"color\":{\"hue\":25,\"saturation\":95,\"x\":0.5267,\"y\":0.4133},\"color_mode\":\"color_temp\",\"color_temp\":500,\"do_not_disturb\":true,\"last_seen\":\"2024-11-23T15:20:15+01:00\",\"linkquality\":217,\"state\":\"OFF\"}"} +{"entity":"EARU Circuit Breaker 32A","payload":"{\"current\":0.1,\"energy\":4.26,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:20:16+01:00\",\"linkquality\":147,\"power\":16,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":31.96,\"voltage\":234}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":254,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:20:16+01:00\",\"linkquality\":163,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":357,\"formaldehyd\":3,\"humidity\":34.5,\"last_seen\":\"2024-11-23T15:20:14+01:00\",\"linkquality\":87,\"temperature\":22,\"voc\":1}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":254,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:20:16+01:00\",\"linkquality\":201,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:20:17+01:00\",\"linkquality\":240,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":9.32,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Moes switch double","payload":"{\"last_seen\":\"2024-11-23T15:20:18+01:00\",\"linkquality\":223,\"power_on_behavior\":\"off\",\"power_on_behavior_l1\":\"off\",\"state_l1\":\"OFF\",\"state_l2\":\"ON\"}"} +{"entity":"Moes switch double","payload":"{\"last_seen\":\"2024-11-23T15:20:18+01:00\",\"linkquality\":223,\"power_on_behavior\":\"off\",\"power_on_behavior_l1\":\"off\",\"state_l1\":\"OFF\",\"state_l2\":\"ON\"}"} +{"entity":"Moes switch double","payload":"{\"last_seen\":\"2024-11-23T15:20:18+01:00\",\"linkquality\":223,\"power_on_behavior\":\"off\",\"power_on_behavior_l1\":\"off\",\"state_l1\":\"OFF\",\"state_l2\":\"ON\"}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:20:18+01:00\",\"linkquality\":240,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":3.64,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Mini luminance motion sensor","payload":"{\"battery\":100,\"illuminance\":2493,\"keep_time\":\"60\",\"last_seen\":\"2024-11-23T15:20:18+01:00\",\"linkquality\":76,\"occupancy\":false,\"sensitivity\":\"high\"}"} +{"entity":"Moes switch double","payload":"{\"last_seen\":\"2024-11-23T15:20:19+01:00\",\"linkquality\":223,\"power_on_behavior\":\"off\",\"power_on_behavior_l1\":\"off\",\"state_l1\":\"OFF\",\"state_l2\":\"ON\"}"} +{"entity":"Moes switch double","payload":"{\"last_seen\":\"2024-11-23T15:20:19+01:00\",\"linkquality\":217,\"power_on_behavior\":\"off\",\"power_on_behavior_l1\":\"off\",\"state_l1\":\"OFF\",\"state_l2\":\"OFF\"}"} +{"entity":"Moes switch double","payload":"{\"last_seen\":\"2024-11-23T15:20:19+01:00\",\"linkquality\":217,\"power_on_behavior\":\"off\",\"power_on_behavior_l1\":\"off\",\"state_l1\":\"OFF\",\"state_l2\":\"OFF\"}"} +{"entity":"Mini luminance motion sensor","payload":"{\"battery\":100,\"illuminance\":2493,\"keep_time\":\"60\",\"last_seen\":\"2024-11-23T15:20:19+01:00\",\"linkquality\":76,\"occupancy\":false,\"sensitivity\":\"high\"}"} +{"entity":"Mini luminance motion sensor","payload":"{\"battery\":100,\"illuminance\":2493,\"keep_time\":\"60\",\"last_seen\":\"2024-11-23T15:20:19+01:00\",\"linkquality\":76,\"occupancy\":false,\"sensitivity\":\"high\"}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":359,\"formaldehyd\":2,\"humidity\":33.8,\"last_seen\":\"2024-11-23T15:20:17+01:00\",\"linkquality\":76,\"temperature\":21.8,\"voc\":4}"} +{"entity":"Mini luminance motion sensor","payload":"{\"battery\":100,\"illuminance\":2538,\"keep_time\":\"60\",\"last_seen\":\"2024-11-23T15:20:19+01:00\",\"linkquality\":76,\"occupancy\":false,\"sensitivity\":\"high\"}"} +{"entity":"Mini luminance motion sensor","payload":"{\"battery\":100,\"illuminance\":2538,\"keep_time\":\"60\",\"last_seen\":\"2024-11-23T15:20:20+01:00\",\"linkquality\":76,\"occupancy\":false,\"sensitivity\":\"high\"}"} +{"entity":"Mini luminance motion sensor","payload":"{\"battery\":100,\"illuminance\":2538,\"keep_time\":\"60\",\"last_seen\":\"2024-11-23T15:20:20+01:00\",\"linkquality\":76,\"occupancy\":false,\"sensitivity\":\"high\"}"} +{"entity":"EARU Circuit Breaker 32A","payload":"{\"current\":0.05,\"energy\":4.26,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:20:21+01:00\",\"linkquality\":141,\"power\":5,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":31.96,\"voltage\":234}"} +{"entity":"Occupancy sensor P1","payload":"{\"battery\":100,\"detection_interval\":30,\"device_temperature\":25,\"illuminance\":547,\"last_seen\":\"2024-11-23T15:20:22+01:00\",\"linkquality\":255,\"motion_sensitivity\":\"medium\",\"occupancy\":true,\"power_outage_count\":34,\"trigger_indicator\":false,\"voltage\":3032}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:20:23+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:20:23+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:20:23+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":358,\"formaldehyd\":4,\"humidity\":33.9,\"last_seen\":\"2024-11-23T15:20:24+01:00\",\"linkquality\":76,\"temperature\":22.1,\"voc\":4}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:20:29+01:00\",\"linkquality\":141,\"motor_reversal\":\"OFF\",\"moving\":\"DOWN\",\"position\":0,\"state\":\"CLOSE\"}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:20:29+01:00\",\"linkquality\":141,\"motor_reversal\":\"OFF\",\"moving\":\"DOWN\",\"position\":0,\"state\":\"CLOSE\"}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:20:29+01:00\",\"linkquality\":168,\"motor_reversal\":\"OFF\",\"moving\":\"DOWN\",\"position\":0,\"state\":\"CLOSE\"}"} +{"entity":"Covers","payload":"{\"state\":\"OPEN\"}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:20:30+01:00\",\"linkquality\":136,\"motor_reversal\":\"OFF\",\"moving\":\"DOWN\",\"position\":10,\"state\":\"OPEN\"}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:20:31+01:00\",\"linkquality\":240,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":3.64,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:20:32+01:00\",\"linkquality\":136,\"motor_reversal\":\"OFF\",\"moving\":\"DOWN\",\"position\":20,\"state\":\"OPEN\"}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:20:33+01:00\",\"linkquality\":136,\"motor_reversal\":\"OFF\",\"moving\":\"DOWN\",\"position\":30,\"state\":\"OPEN\"}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":359,\"formaldehyd\":1,\"humidity\":34.4,\"last_seen\":\"2024-11-23T15:20:32+01:00\",\"linkquality\":103,\"temperature\":21.9,\"voc\":4}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:20:35+01:00\",\"linkquality\":130,\"motor_reversal\":\"OFF\",\"moving\":\"DOWN\",\"position\":40,\"state\":\"OPEN\"}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:20:36+01:00\",\"linkquality\":130,\"motor_reversal\":\"OFF\",\"moving\":\"DOWN\",\"position\":50,\"state\":\"OPEN\"}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":358,\"formaldehyd\":4,\"humidity\":34.5,\"last_seen\":\"2024-11-23T15:20:35+01:00\",\"linkquality\":91,\"temperature\":22.1,\"voc\":4}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:20:38+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:20:38+01:00\",\"linkquality\":130,\"motor_reversal\":\"OFF\",\"moving\":\"DOWN\",\"position\":60,\"state\":\"OPEN\"}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:20:38+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:20:38+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:20:39+01:00\",\"linkquality\":130,\"motor_reversal\":\"OFF\",\"moving\":\"DOWN\",\"position\":70,\"state\":\"OPEN\"}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:20:41+01:00\",\"linkquality\":130,\"motor_reversal\":\"OFF\",\"moving\":\"DOWN\",\"position\":80,\"state\":\"OPEN\"}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:20:42+01:00\",\"linkquality\":130,\"motor_reversal\":\"OFF\",\"moving\":\"DOWN\",\"position\":90,\"state\":\"OPEN\"}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":359,\"formaldehyd\":1,\"humidity\":34.5,\"last_seen\":\"2024-11-23T15:20:42+01:00\",\"linkquality\":91,\"temperature\":21.8,\"voc\":5}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:20:44+01:00\",\"linkquality\":136,\"motor_reversal\":\"OFF\",\"moving\":\"STOP\",\"position\":90,\"state\":\"OPEN\"}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:20:44+01:00\",\"linkquality\":234,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":3.64,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Window shutter","payload":"{\"calibration_time\":15,\"last_seen\":\"2024-11-23T15:20:44+01:00\",\"linkquality\":136,\"motor_reversal\":\"OFF\",\"moving\":\"STOP\",\"position\":100,\"state\":\"OPEN\"}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":254,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:20:46+01:00\",\"linkquality\":180,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"EARU Circuit Breaker 32A","payload":"{\"current\":0.06,\"energy\":4.26,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:20:46+01:00\",\"linkquality\":180,\"power\":6,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":31.96,\"voltage\":234}"} +{"entity":"EARU Circuit Breaker 32A","payload":"{\"current\":0.06,\"energy\":4.26,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:20:46+01:00\",\"linkquality\":180,\"power\":6,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":32.03,\"voltage\":234}"} +{"entity":"EARU Circuit Breaker 32A","payload":"{\"current\":0.06,\"energy\":4.26,\"indicator_mode\":\"off\",\"last_seen\":\"2024-11-23T15:20:46+01:00\",\"linkquality\":180,\"power\":6,\"power_outage_memory\":\"restore\",\"state\":\"ON\",\"temperature\":32.03,\"voltage\":234}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":254,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:20:46+01:00\",\"linkquality\":201,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":362,\"formaldehyd\":4,\"humidity\":34,\"last_seen\":\"2024-11-23T15:20:45+01:00\",\"linkquality\":91,\"temperature\":22.1,\"voc\":5}"} +{"entity":"Climate sensor","payload":"{\"battery\":37,\"humidity\":38.41,\"last_seen\":\"2024-11-23T15:20:48+01:00\",\"linkquality\":255,\"power_outage_count\":77,\"pressure\":1005,\"temperature\":22.89,\"voltage\":2905}"} +{"entity":"Climate sensor","payload":"{\"battery\":37,\"humidity\":32.39,\"last_seen\":\"2024-11-23T15:20:48+01:00\",\"linkquality\":255,\"power_outage_count\":77,\"pressure\":1005,\"temperature\":22.89,\"voltage\":2905}"} +{"entity":"Climate sensor","payload":"{\"battery\":37,\"humidity\":32.39,\"last_seen\":\"2024-11-23T15:20:48+01:00\",\"linkquality\":255,\"power_outage_count\":77,\"pressure\":1005.1,\"temperature\":22.89,\"voltage\":2905}"} +{"entity":"Develco air quality","payload":"{\"air_quality\":\"moderate\",\"battery\":20,\"humidity\":30,\"last_seen\":\"2024-11-23T15:20:49+01:00\",\"linkquality\":255,\"temperature\":22.7,\"update\":{\"installed_version\":262145,\"latest_version\":262145,\"state\":\"idle\"},\"voc\":1080,\"voltage\":2600}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:20:50+01:00\",\"linkquality\":240,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.43,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":362,\"formaldehyd\":3,\"humidity\":33.9,\"last_seen\":\"2024-11-23T15:20:50+01:00\",\"linkquality\":103,\"temperature\":22.1,\"voc\":4}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":360,\"formaldehyd\":2,\"humidity\":34.2,\"last_seen\":\"2024-11-23T15:20:52+01:00\",\"linkquality\":103,\"temperature\":21.8,\"voc\":1}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":50,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:20:52+01:00\",\"linkquality\":213,\"local_temperature\":28.5,\"max_temperature_limit\":28,\"min_temperature_limit\":4,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":359,\"formaldehyd\":2,\"humidity\":34.5,\"last_seen\":\"2024-11-23T15:21:00+01:00\",\"linkquality\":91,\"temperature\":22,\"voc\":2}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:21:03+01:00\",\"linkquality\":240,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.43,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Occupancy sensor P1","payload":"{\"battery\":100,\"detection_interval\":30,\"device_temperature\":25,\"illuminance\":542,\"last_seen\":\"2024-11-23T15:21:07+01:00\",\"linkquality\":255,\"motion_sensitivity\":\"medium\",\"occupancy\":true,\"power_outage_count\":34,\"trigger_indicator\":false,\"voltage\":3032}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":25,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:21:05+01:00\",\"linkquality\":201,\"local_temperature\":28.5,\"max_temperature_limit\":28,\"min_temperature_limit\":4,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Moes thermo","service":"set","payload":"{\"current_heating_setpoint\":25}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:21:07+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"medium\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:21:07+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Presence sensor","payload":"{\"fading_time\":60,\"illuminance\":2934,\"indicator\":\"ON\",\"large_motion_detection_distance\":9,\"large_motion_detection_sensitivity\":8,\"last_seen\":\"2024-11-23T15:21:07+01:00\",\"linkquality\":255,\"medium_motion_detection_distance\":6,\"medium_motion_detection_sensitivity\":8,\"motion_state\":\"large\",\"presence\":true,\"small_detection_distance\":6,\"small_detection_sensitivity\":8}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":25,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:21:07+01:00\",\"linkquality\":207,\"local_temperature\":28.5,\"max_temperature_limit\":28,\"min_temperature_limit\":4,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":361,\"formaldehyd\":4,\"humidity\":34.1,\"last_seen\":\"2024-11-23T15:21:07+01:00\",\"linkquality\":87,\"temperature\":22,\"voc\":3}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":364,\"formaldehyd\":4,\"humidity\":33.9,\"last_seen\":\"2024-11-23T15:21:10+01:00\",\"linkquality\":87,\"temperature\":22,\"voc\":2}"} +{"entity":"Moes thermo","payload":"{\"child_lock\":\"UNLOCK\",\"current_heating_setpoint\":25,\"heat\":\"ON\",\"last_seen\":\"2024-11-23T15:21:13+01:00\",\"linkquality\":207,\"local_temperature\":28.5,\"max_temperature_limit\":28,\"min_temperature_limit\":4,\"preset\":\"hold\",\"preset_mode\":\"hold\",\"running_state\":\"heat\",\"sensor\":\"IN\",\"system_mode\":\"heat\"}"} +{"entity":"Gledopto RGBCTT light II","payload":"{\"brightness\":254,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":495,\"last_seen\":\"2024-11-23T15:21:16+01:00\",\"linkquality\":174,\"state\":\"OFF\",\"update\":{\"installed_version\":419508225,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Aqara Dual relay module T2","payload":"{\"consumption\":1.921302978515625,\"current\":0.02,\"device_temperature\":40,\"energy\":1.921,\"interlock\":\"OFF\",\"last_seen\":\"2024-11-23T15:21:16+01:00\",\"linkquality\":234,\"mode\":\"power\",\"operation_mode_l1\":\"control_relay\",\"operation_mode_l2\":\"control_relay\",\"power\":0.43,\"power_on_behavior\":\"previous\",\"power_outage_count\":64,\"pulse_length\":200,\"state\":\"ON\",\"state_l1\":\"ON\",\"state_l2\":\"ON\",\"switch_type\":\"momentary\",\"voltage\":227.82}"} +{"entity":"Gledopto RGBCTT light I","payload":"{\"brightness\":254,\"color\":{\"hue\":0,\"saturation\":0,\"x\":0,\"y\":0},\"color_mode\":\"color_temp\",\"color_temp\":495,\"color_temp_startup\":65535,\"last_seen\":\"2024-11-23T15:21:16+01:00\",\"linkquality\":213,\"state\":\"OFF\",\"update\":{\"installed_version\":385953793,\"latest_version\":385953793,\"state\":\"idle\"}}"} +{"entity":"Occupancy sensor","payload":"{\"battery\":100,\"device_temperature\":29,\"illuminance\":239,\"illuminance_lux\":239,\"last_seen\":\"2024-11-23T15:21:17+01:00\",\"linkquality\":108,\"occupancy\":true,\"power_outage_count\":548,\"voltage\":3035}"} +{"entity":"Occupancy sensor","payload":"{\"battery\":100,\"device_temperature\":29,\"illuminance\":239,\"illuminance_lux\":239,\"last_seen\":\"2024-11-23T15:21:17+01:00\",\"linkquality\":108,\"occupancy\":true,\"power_outage_count\":548,\"voltage\":3035}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":363,\"formaldehyd\":3,\"humidity\":34,\"last_seen\":\"2024-11-23T15:21:17+01:00\",\"linkquality\":114,\"temperature\":21.8,\"voc\":3}"} +{"entity":"Tuya Air Quality Sensor (_TZE200_yvx5lh6k)","payload":"{\"co2\":363,\"formaldehyd\":1,\"humidity\":33.8,\"last_seen\":\"2024-11-23T15:21:21+01:00\",\"linkquality\":87,\"temperature\":22,\"voc\":4}"} diff --git a/src/platform.test.ts b/src/platform.test.ts index d1bb3e8..b20017e 100644 --- a/src/platform.test.ts +++ b/src/platform.test.ts @@ -1,63 +1,271 @@ +/* eslint-disable no-console */ +/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unused-vars */ import { Matterbridge, PlatformConfig } from 'matterbridge'; -import { AnsiLogger } from 'matterbridge/logger'; +import { AnsiLogger, idn, ign, LogLevel, rs, TimestampFormat, wr } from 'matterbridge/logger'; import { ZigbeePlatform } from './platform'; import { jest } from '@jest/globals'; +import { BridgeDevice, BridgeGroup, BridgeInfo } from './zigbee2mqttTypes'; +import path from 'path'; +import fs from 'fs'; +import { Zigbee2MQTT } from './zigbee2mqtt'; +import exp from 'constants'; +import { wait } from '../../matterbridge/dist/utils/utils'; describe('TestPlatform', () => { let mockMatterbridge: Matterbridge; - let mockLog: AnsiLogger; let mockConfig: PlatformConfig; - let testPlatform: ZigbeePlatform; + let z2mPlatform: ZigbeePlatform; - // const log = new AnsiLogger({ logName: 'shellyDeviceTest', logTimestampFormat: TimestampFormat.TIME_MILLIS, logDebug: true }); + let loggerLogSpy: jest.SpiedFunction<(level: LogLevel, message: string, ...parameters: any[]) => void>; + let consoleLogSpy: jest.SpiedFunction<(...args: any[]) => void>; + let z2mStartSpy: jest.SpiedFunction<() => Promise>; + let z2mStopSpy: jest.SpiedFunction<() => Promise>; + let z2mSubscribeSpy: jest.SpiedFunction<(topic: string) => Promise>; + let z2mPublishSpy: jest.SpiedFunction<(topic: string, message: string, queue: boolean) => Promise>; + const log = new AnsiLogger({ logName: 'ZigbeeTest', logTimestampFormat: TimestampFormat.TIME_MILLIS, logLevel: LogLevel.DEBUG }); - beforeEach(() => { + beforeAll(() => { mockMatterbridge = { addBridgedDevice: jest.fn(), matterbridgeDirectory: '', matterbridgePluginDirectory: 'temp', systemInformation: { ipv4Address: undefined }, - matterbridgeVersion: '1.6.1', + matterbridgeVersion: '1.6.2', removeAllBridgedDevices: jest.fn(), } as unknown as Matterbridge; - mockLog = { error: jest.fn(), warn: jest.fn(), info: jest.fn(), debug: jest.fn() } as unknown as AnsiLogger; mockConfig = { - 'name': 'matterbridge-test', + 'name': 'matterbridge-zigbee2mqtt', 'type': 'DynamicPlatform', - 'noDevices': false, - 'throwLoad': false, - 'throwStart': false, - 'throwConfigure': false, - 'throwShutdown': false, + 'topic': 'zigbee2mqtt', + 'host': 'localhost', + 'port': 1883, + 'protocolVersion': 5, + 'username': undefined, + 'password': undefined, + 'blackList': [], + 'whiteList': [], + 'switchList': [], + 'lightList': [], + 'outletList': [], + 'featureBlackList': ['device_temperature', 'update', 'update_available', 'power_outage_count', 'indicator_mode', 'do_not_disturb', 'color_temp_startup'], + 'deviceFeatureBlackList': {}, + 'postfixHostname': true, + 'debug': true, 'unregisterOnShutdown': false, - 'delayStart': false, } as PlatformConfig; - testPlatform = new ZigbeePlatform(mockMatterbridge, mockLog, mockConfig); + // Spy on and mock the AnsiLogger.log method + loggerLogSpy = jest.spyOn(AnsiLogger.prototype, 'log').mockImplementation((level: string, message: string, ...parameters: any[]) => { + // console.log(`Mocked log: ${level} - ${message}`, ...parameters); + }); + consoleLogSpy = jest.spyOn(console, 'log').mockImplementation((...args: any[]) => { + // Mock implementation or empty function + }); + + z2mStartSpy = jest.spyOn(Zigbee2MQTT.prototype, 'start').mockImplementation(() => { + console.log('Mocked start'); + return Promise.resolve(); + }); + z2mStopSpy = jest.spyOn(Zigbee2MQTT.prototype, 'stop').mockImplementation(() => { + console.log('Mocked stop'); + return Promise.resolve(); + }); + z2mSubscribeSpy = jest.spyOn(Zigbee2MQTT.prototype, 'subscribe').mockImplementation((topic: string) => { + console.log('Mocked subscribe', topic); + return Promise.resolve(); + }); + z2mPublishSpy = jest.spyOn(Zigbee2MQTT.prototype, 'publish').mockImplementation((topic: string, message: string, queue?: boolean) => { + console.log(`Mocked publish: ${topic} - ${message} queue ${queue}`); + return Promise.resolve(); + }); + }); + + beforeEach(() => { + // Clears the call history before each test + loggerLogSpy.mockClear(); + consoleLogSpy.mockClear(); + }); + + afterAll(() => { + // Restore the original implementation of the AnsiLogger.log method + loggerLogSpy.mockRestore(); + consoleLogSpy.mockRestore(); + z2mStartSpy.mockRestore(); + z2mStopSpy.mockRestore(); + z2mSubscribeSpy.mockRestore(); + z2mPublishSpy.mockRestore(); }); it('should initialize platform with config name', () => { - mockConfig.noDevices = true; - mockConfig.delayStart = true; - testPlatform = new ZigbeePlatform(mockMatterbridge, mockLog, mockConfig); - expect(mockLog.info).toHaveBeenCalledTimes(6); + z2mPlatform = new ZigbeePlatform(mockMatterbridge, log, mockConfig); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringMatching(/^Initializing platform:/)); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringMatching(/^Loaded zigbee2mqtt parameters/)); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringMatching(/^Connecting to MQTT broker/)); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.DEBUG, expect.stringMatching(/^Created zigbee2mqtt dynamic platform/)); }); - // eslint-disable-next-line jest/no-commented-out-tests - /* + it('should call onStart with reason', async () => { - await testPlatform.onStart('Test reason'); - expect(mockLog.info).toHaveBeenCalledWith('onStart called with reason:', 'Test reason'); + const info = z2mPlatform.z2m.readConfig(path.join('src', 'mock', 'bridge-info.json')); + expect(info).toBeDefined(); + const devices = z2mPlatform.z2m.readConfig(path.join('src', 'mock', 'bridge-devices.json')); + expect(devices).toBeDefined(); + const groups = z2mPlatform.z2m.readConfig(path.join('src', 'mock', 'bridge-groups.json')); + expect(groups).toBeDefined(); + + z2mPlatform.z2mBridgeOnline = true; + z2mPlatform.z2mBridgeInfo = info as BridgeInfo; + z2mPlatform.z2mBridgeDevices = devices as BridgeDevice[]; + z2mPlatform.z2mBridgeGroups = groups as BridgeGroup[]; + + await z2mPlatform.onStart('Jest Test'); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringMatching(/^Started zigbee2mqtt dynamic platform/)); + }, 60000); + + it('should have registered devices', async () => { + expect(z2mPlatform.z2mBridgeDevices).toBeDefined(); + if (!z2mPlatform.z2mBridgeDevices) return; + expect(z2mPlatform.z2mBridgeDevices.length).toBe(34); + }); + + it('should have registered groups', async () => { + expect(z2mPlatform.z2mBridgeGroups).toBeDefined(); + if (!z2mPlatform.z2mBridgeGroups) return; + expect(z2mPlatform.z2mBridgeGroups.length).toBe(10); + }); + + it('should update entity OFFLINE', async () => { + for (const entity of z2mPlatform.zigbeeEntities) { + expect(entity).toBeDefined(); + expect(entity.entityName).toBeDefined(); + expect(entity.entityName.length).toBeGreaterThan(0); + z2mPlatform.z2m.emit('OFFLINE-' + entity.entityName); + await wait(200); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.WARN, `OFFLINE message for device ${(entity as any).ien}${entity.entityName}${rs}`); + } + }, 60000); + + it('should update entity ONLINE', async () => { + for (const entity of z2mPlatform.zigbeeEntities) { + expect(entity).toBeDefined(); + expect(entity.entityName).toBeDefined(); + expect(entity.entityName.length).toBeGreaterThan(0); + z2mPlatform.z2m.emit('ONLINE-' + entity.entityName); + await wait(200); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, `ONLINE message for device ${(entity as any).ien}${entity.entityName}${rs}`); + } + }, 60000); + + it('should update entity MESSAGE', async () => { + const filePath = path.join('src', 'mock', 'bridge-payloads.txt'); + const fileContent = fs.readFileSync(filePath, 'utf-8'); + const logEntries = fileContent + .split('\n') + .filter((line) => line.trim() !== '') + .map((line) => JSON.parse(line)); + expect(logEntries).toBeDefined(); + expect(logEntries.length).toBe(702); + + logEntries.forEach((entry: { entity: string; service: string; payload: string }) => { + const payloadJson: Record = JSON.parse(entry.payload); + const entity = z2mPlatform.zigbeeEntities.find((entity) => entity.entityName === entry.entity); + expect(entity).toBeDefined(); + if (!entity) return; + expect(entity.entityName).toBeDefined(); + expect(entity.entityName.length).toBeGreaterThan(0); + if (entity) { + z2mPlatform.z2m.emit('MESSAGE-' + entity.entityName, payloadJson); + } + }); + }, 60000); + + it('should update /bridge/state online', async () => { + z2mPlatform.z2mBridgeOnline = false; + (z2mPlatform.z2m as any).messageHandler('zigbee2mqtt/bridge/state', Buffer.from('{"state":"online"}')); + expect(z2mPlatform.z2mBridgeOnline).toBe(true); + z2mPlatform.z2mBridgeOnline = false; + (z2mPlatform.z2m as any).messageHandler('zigbee2mqtt/bridge/state', Buffer.from('online')); + expect(z2mPlatform.z2mBridgeOnline).toBe(true); + }); + + it('should update /bridge/state offline', async () => { + z2mPlatform.z2mBridgeOnline = true; + (z2mPlatform.z2m as any).messageHandler('zigbee2mqtt/bridge/state', Buffer.from('{"state":"offline"}')); + expect(z2mPlatform.z2mBridgeOnline).toBe(false); + z2mPlatform.z2mBridgeOnline = true; + (z2mPlatform.z2m as any).messageHandler('zigbee2mqtt/bridge/state', Buffer.from('offline')); + expect(z2mPlatform.z2mBridgeOnline).toBe(false); + }); + + it('should update /bridge/info', async () => { + const info = z2mPlatform.z2m.readConfig(path.join('src', 'mock', 'bridge-info.json')); + expect(info).toBeDefined(); + + z2mPlatform.z2mBridgeInfo = undefined; + (z2mPlatform.z2m as any).messageHandler('zigbee2mqtt/bridge/info', Buffer.from(JSON.stringify(info))); + expect(z2mPlatform.z2mBridgeInfo).toBeDefined(); + await wait(500); + }); + + it('should update /bridge/devices', async () => { + const devices = z2mPlatform.z2m.readConfig(path.join('src', 'mock', 'bridge-devices.json')); + expect(devices).toBeDefined(); + + z2mPlatform.z2mBridgeDevices = undefined; + (z2mPlatform.z2m as any).messageHandler('zigbee2mqtt/bridge/devices', Buffer.from(JSON.stringify(devices))); + expect(z2mPlatform.z2mBridgeDevices).toBeDefined(); + if (!z2mPlatform.z2mBridgeDevices) return; + expect((z2mPlatform.z2mBridgeDevices as BridgeDevice[]).length).toBe(34); + await wait(500); + }); + + it('should update /bridge/groups', async () => { + const groups = z2mPlatform.z2m.readConfig(path.join('src', 'mock', 'bridge-groups.json')); + expect(groups).toBeDefined(); + + z2mPlatform.z2mBridgeGroups = undefined; + (z2mPlatform.z2m as any).messageHandler('zigbee2mqtt/bridge/groups', Buffer.from(JSON.stringify(groups))); + expect(z2mPlatform.z2mBridgeGroups).toBeDefined(); + if (!z2mPlatform.z2mBridgeGroups) return; + expect((z2mPlatform.z2mBridgeGroups as BridgeGroup[]).length).toBe(10); + await wait(500); + }); + + it('should update /Moes thermo/availability online', async () => { + (z2mPlatform.z2m as any).messageHandler('zigbee2mqtt/Moes thermo/availability', Buffer.from('{"state":"online"}')); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, `ONLINE message for device ${idn}Moes thermo${rs}`); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, `zigbee2MQTT device Moes thermo is online`); + await wait(200); + }); + + it('should update /Moes thermo/availability offline', async () => { + (z2mPlatform.z2m as any).messageHandler('zigbee2mqtt/Moes thermo/availability', Buffer.from('{"state":"offline"}')); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.WARN, `OFFLINE message for device ${idn}Moes thermo${rs}`); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.WARN, `zigbee2MQTT device Moes thermo is offline`); + await wait(200); + }); + + it('should update /At home/availability online', async () => { + (z2mPlatform.z2m as any).messageHandler('zigbee2mqtt/At home/availability', Buffer.from('{"state":"online"}')); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, `ONLINE message for device ${ign}At home${rs}`); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, `zigbee2MQTT device At home is online`); + await wait(200); + }); + + it('should update /At home/availability offline', async () => { + (z2mPlatform.z2m as any).messageHandler('zigbee2mqtt/At home/availability', Buffer.from('{"state":"offline"}')); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.WARN, `OFFLINE message for device ${ign}At home${rs}`); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.WARN, `zigbee2MQTT device At home is offline`); + await wait(200); }); it('should call onConfigure', async () => { - await testPlatform.onConfigure(); - expect(mockLog.info).toHaveBeenCalledWith('onConfigure called'); + await z2mPlatform.onConfigure(); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringMatching(/^Configured zigbee2mqtt dynamic platform/)); }); it('should call onShutdown with reason', async () => { - await testPlatform.onShutdown('Test reason'); - expect(mockLog.info).toHaveBeenCalledWith('onShutdown called with reason:', 'Test reason'); + await z2mPlatform.onShutdown('Jest Test'); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringMatching(/^Shutdown zigbee2mqtt dynamic platform/)); }); - */ }); diff --git a/src/platform.ts b/src/platform.ts index 542e83f..e265d78 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -4,9 +4,9 @@ * @file platform.ts * @author Luca Liguori * @date 2023-12-29 - * @version 2.1.1 + * @version 2.2.2 * - * Copyright 2023, 2024 Luca Liguori. + * Copyright 2023, 2024, 2025 Luca Liguori. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,11 +23,11 @@ import { BridgedDeviceBasicInformationCluster, DoorLock, DoorLockCluster, Endpoint, Matterbridge, MatterbridgeDevice, MatterbridgeDynamicPlatform, PlatformConfig } from 'matterbridge'; import { AnsiLogger, dn, gn, db, wr, zb, payloadStringify, rs, debugStringify, CYAN, er } from 'matterbridge/logger'; -import { waiter } from 'matterbridge/utils'; +import { isValidNumber, isValidString, waiter } from 'matterbridge/utils'; import path from 'path'; -import { ZigbeeDevice, ZigbeeEntity, ZigbeeGroup, BridgedBaseDevice } from './entity.js'; +import { ZigbeeDevice, ZigbeeEntity, ZigbeeGroup /* , BridgedBaseDevice*/ } from './entity.js'; import { Zigbee2MQTT } from './zigbee2mqtt.js'; import { BridgeInfo, BridgeDevice, BridgeGroup } from './zigbee2mqttTypes.js'; import { Payload } from './payloadTypes.js'; @@ -40,8 +40,8 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { private permitJoinCallBack: ((entityName: string, permit: boolean) => Promise) | undefined = undefined; // platform - private bridgedDevices: BridgedBaseDevice[] = []; - private zigbeeEntities: ZigbeeEntity[] = []; + public bridgedDevices: MatterbridgeDevice[] = []; + public zigbeeEntities: ZigbeeEntity[] = []; private injectTimer: NodeJS.Timeout | undefined; // z2m @@ -82,7 +82,7 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { throw new Error(`This plugin requires Matterbridge version >= "1.6.1". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend."`); } - this.log.debug(`Config:')}${rs}`, config); + // this.log.debug(`Config:')}${rs}`, config); this.debugEnabled = config.debug as boolean; this.shouldStart = false; this.shouldConfigure = false; @@ -122,14 +122,18 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { this.log.info(`Initializing platform: ${this.config.name}${rs} v${CYAN}${this.version}`); this.log.info(`Loaded zigbee2mqtt parameters from ${path.join(matterbridge.matterbridgeDirectory, 'matterbridge-zigbee2mqtt.config.json')}${rs}:`); - this.log.debug(`Config:')}${rs}`, config); + // this.log.debug(`Config:')}${rs}`, config); this.z2m = new Zigbee2MQTT(this.mqttHost, this.mqttPort, this.mqttTopic, this.mqttUsername, this.mqttPassword, this.mqttProtocol, this.debugEnabled); this.z2m.setLogDebug(this.debugEnabled); this.z2m.setDataPath(path.join(matterbridge.matterbridgePluginDirectory, 'matterbridge-zigbee2mqtt')); this.log.info(`Connecting to MQTT broker: ${'mqtt://' + this.mqttHost + ':' + this.mqttPort.toString()}`); - this.z2m.start(); + if (isValidString(this.mqttHost) && isValidNumber(this.mqttPort, 1, 65535)) { + this.z2m.start(); + } else { + this.log.error(`Invalid MQTT broker host: ${this.mqttHost} or port: ${this.mqttPort}`); + } this.z2m.on('mqtt_connect', () => { this.log.info(`MQTT broker at ${this.z2m.mqttHost}:${this.z2m.mqttPort} connected`); @@ -196,9 +200,7 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { this.log.info(`Configuring ${this.zigbeeEntities.length} zigbee entities.`); for (const bridgedEntity of this.zigbeeEntities) { if (bridgedEntity.isDevice && bridgedEntity.device) await this.requestDeviceUpdate(bridgedEntity.device); - } - for (const device of this.bridgedDevices) { - device.configure(); + bridgedEntity.configure(); } } }); @@ -220,9 +222,7 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { this.log.info(`Configuring ${this.zigbeeEntities.length} zigbee entities.`); for (const bridgedEntity of this.zigbeeEntities) { if (bridgedEntity.isGroup && bridgedEntity.group) await this.requestGroupUpdate(bridgedEntity.group); - } - for (const device of this.bridgedDevices) { - device.configure(); + bridgedEntity.configure(); } } }); @@ -236,16 +236,16 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { this.z2m.on('permit_join', async (device: string, time: number, status: boolean) => { this.log.info(`zigbee2MQTT sent permit_join device: ${device} time: ${time} status: ${status}`); for (const zigbeeEntity of this.zigbeeEntities) { - if (zigbeeEntity.bridgedDevice?.isRouter && (device === undefined || device === zigbeeEntity.bridgedDevice.deviceName)) { - this.log.info(`*- ${zigbeeEntity.bridgedDevice.deviceName} ${zigbeeEntity.bridgedDevice.number} (${zigbeeEntity.bridgedDevice.name})`); + if (zigbeeEntity.isRouter && (device === undefined || device === zigbeeEntity.bridgedDevice?.deviceName)) { + this.log.info(`*- ${zigbeeEntity.bridgedDevice?.deviceName} ${zigbeeEntity.bridgedDevice?.number} (${zigbeeEntity.bridgedDevice?.name})`); if (zigbeeEntity.device && status) { - zigbeeEntity.bridgedDevice.getClusterServer(DoorLockCluster)?.setLockStateAttribute(DoorLock.LockState.Unlocked); - zigbeeEntity.bridgedDevice.getClusterServer(DoorLockCluster)?.triggerLockOperationEvent({ lockOperationType: DoorLock.LockOperationType.Unlock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }); + zigbeeEntity.bridgedDevice?.getClusterServer(DoorLockCluster)?.setLockStateAttribute(DoorLock.LockState.Unlocked); + zigbeeEntity.bridgedDevice?.getClusterServer(DoorLockCluster)?.triggerLockOperationEvent({ lockOperationType: DoorLock.LockOperationType.Unlock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }); this.log.info(`Device ${zigbeeEntity.entityName} unlocked`); } if (zigbeeEntity.device && !status) { - zigbeeEntity.bridgedDevice.getClusterServer(DoorLockCluster)?.setLockStateAttribute(DoorLock.LockState.Locked); - zigbeeEntity.bridgedDevice.getClusterServer(DoorLockCluster)?.triggerLockOperationEvent({ lockOperationType: DoorLock.LockOperationType.Lock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }); + zigbeeEntity.bridgedDevice?.getClusterServer(DoorLockCluster)?.setLockStateAttribute(DoorLock.LockState.Locked); + zigbeeEntity.bridgedDevice?.getClusterServer(DoorLockCluster)?.triggerLockOperationEvent({ lockOperationType: DoorLock.LockOperationType.Lock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }); this.log.info(`Device ${zigbeeEntity.entityName} locked`); } } @@ -372,33 +372,22 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { } override async onConfigure() { - /* - if (!this.z2mDevicesRegistered || !this.z2mGroupsRegistered) { - this.shouldConfigure = true; - this.log.debug('Setting flag to configure when zigbee2mqtt sends devices'); - } - */ - this.log.info(`Requesting update for ${this.zigbeeEntities.length} zigbee entities.`); for (const bridgedEntity of this.zigbeeEntities) { if (bridgedEntity.isDevice && bridgedEntity.device) await this.requestDeviceUpdate(bridgedEntity.device); if (bridgedEntity.isGroup && bridgedEntity.group) await this.requestGroupUpdate(bridgedEntity.group); - } - - this.log.info(`Configuring ${this.bridgedDevices.length} matter devices.`); - for (const device of this.bridgedDevices) { - device.configure(); - } - for (const device of this.bridgedDevices.filter((device) => device.isRouter)) { - this.log.info(`Configuring router ${device.deviceName}.`); - if (this.z2mBridgeInfo?.permit_join) { - device.getClusterServer(DoorLockCluster)?.setLockStateAttribute(DoorLock.LockState.Unlocked); - device.getClusterServer(DoorLockCluster)?.triggerLockOperationEvent({ lockOperationType: DoorLock.LockOperationType.Unlock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }); - } else { - device.getClusterServer(DoorLockCluster)?.setLockStateAttribute(DoorLock.LockState.Locked); - device.getClusterServer(DoorLockCluster)?.triggerLockOperationEvent({ lockOperationType: DoorLock.LockOperationType.Lock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }); + bridgedEntity.configure(); + if (bridgedEntity.isRouter && bridgedEntity.bridgedDevice) { + this.log.info(`Configuring router ${bridgedEntity.bridgedDevice?.deviceName}.`); + if (this.z2mBridgeInfo?.permit_join) { + bridgedEntity.bridgedDevice.getClusterServer(DoorLockCluster)?.setLockStateAttribute(DoorLock.LockState.Unlocked); + if (bridgedEntity.bridgedDevice.number) + bridgedEntity.bridgedDevice.getClusterServer(DoorLockCluster)?.triggerLockOperationEvent({ lockOperationType: DoorLock.LockOperationType.Unlock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }); + } else { + bridgedEntity.bridgedDevice.getClusterServer(DoorLockCluster)?.setLockStateAttribute(DoorLock.LockState.Locked); + if (bridgedEntity.bridgedDevice.number) bridgedEntity.bridgedDevice.getClusterServer(DoorLockCluster)?.triggerLockOperationEvent({ lockOperationType: DoorLock.LockOperationType.Lock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }); + } } - device.configure(); } this.availabilityTimer = setTimeout(() => { @@ -417,10 +406,15 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { } }, 60 * 1000); } + this.log.info(`Configured zigbee2mqtt dynamic platform v${this.version}`); } override async onShutdown(reason?: string) { this.log.debug('Shutting down zigbee2mqtt platform: ' + reason); + for (const entity of this.zigbeeEntities) { + entity.destroy(); + } + if (this.injectTimer) clearInterval(this.injectTimer); this.injectTimer = undefined; if (this.availabilityTimer) clearInterval(this.availabilityTimer); @@ -429,6 +423,7 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { if (this.config.unregisterOnShutdown === true) await this.unregisterAllDevices(); this.z2m.stop(); this.publishCallBack = undefined; + this.log.info(`Shutdown zigbee2mqtt dynamic platform v${this.version}`); } /** @@ -549,12 +544,6 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { } private async unregisterZigbeeEntity(friendly_name: string) { - /* - for (const zigbeeEntity of this.zigbeeEntities) { - if (zigbeeEntity.entityName === friendly_name) this.log.warn(`***Found device: ${friendly_name}`); - else this.log.info(`**Device: ${zigbeeEntity.entityName}`); - } - */ const entity = this.zigbeeEntities.find((entity) => entity.entityName === friendly_name); if (entity) { this.log.info(`Removing device: ${friendly_name}`); @@ -562,12 +551,6 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { this.zigbeeEntities = this.zigbeeEntities.filter((entity) => entity.entityName !== friendly_name); this.bridgedDevices = this.bridgedDevices.filter((device) => device.deviceName !== friendly_name); } - /* - for (const zigbeeEntity of this.zigbeeEntities) { - if (zigbeeEntity.entityName === friendly_name) this.log.warn(`***Found device: ${friendly_name}`); - else this.log.info(`**Device: ${zigbeeEntity.entityName}`); - } - */ } private updateAvailability(available: boolean) { @@ -575,7 +558,7 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { this.log.info(`Setting availability for ${this.bridgedDevices.length} devices to ${available}`); for (const bridgedDevice of this.bridgedDevices) { bridgedDevice.getClusterServer(BridgedDeviceBasicInformationCluster)?.setReachableAttribute(available); - bridgedDevice.getClusterServer(BridgedDeviceBasicInformationCluster)?.triggerReachableChangedEvent({ reachableNewValue: available }); + if (bridgedDevice.number) bridgedDevice.getClusterServer(BridgedDeviceBasicInformationCluster)?.triggerReachableChangedEvent({ reachableNewValue: available }); } } } diff --git a/src/zigbee2mqtt.ts b/src/zigbee2mqtt.ts index f250251..78d37be 100644 --- a/src/zigbee2mqtt.ts +++ b/src/zigbee2mqtt.ts @@ -4,7 +4,7 @@ * @file zigbee2mqtt.ts * @author Luca Liguori * @date 2023-06-30 - * @version 2.3.2 + * @version 2.3.3 * * Copyright 2023, 2024, 2025 Luca Liguori. * @@ -257,6 +257,7 @@ export class Zigbee2MQTT extends EventEmitter { private z2mVersion: string; public z2mDevices: z2mDevice[]; public z2mGroups: z2mGroup[]; + loggedEntries = 0; // Define our MQTT options private options: IClientOptions = { @@ -603,13 +604,13 @@ export class Zigbee2MQTT extends EventEmitter { if (data.config.advanced.legacy_api === true) this.log.info(`Message bridge/info advanced.legacy_api is ${data.config.advanced.legacy_api}`); if (data.config.advanced.legacy_availability_payload === true) this.log.info(`Message bridge/info advanced.legacy_availability_payload is ${data.config.advanced.legacy_availability_payload}`); this.emit('info', this.z2mVersion, this.z2mIsAvailabilityEnabled, this.z2mPermitJoin, this.z2mPermitJoinTimeout); - this.writeBufferJSON('bridge-info', payload); this.emit('bridge-info', data); + if (this.log.logLevel === LogLevel.DEBUG) this.writeBufferJSON('bridge-info', payload); } else if (topic.startsWith(this.mqttTopic + '/bridge/devices')) { this.z2mDevices.splice(0, this.z2mDevices.length); const devices: Device[] = this.tryJsonParse(payload.toString()); const data = this.tryJsonParse(payload.toString()); - this.writeBufferJSON('bridge-devices', payload); + if (this.log.logLevel === LogLevel.DEBUG) this.writeBufferJSON('bridge-devices', payload); this.emit('bridge-devices', data); let index = 1; for (const device of devices) { @@ -708,7 +709,7 @@ export class Zigbee2MQTT extends EventEmitter { this.z2mGroups.splice(0, this.z2mGroups.length); const groups: Group[] = this.tryJsonParse(payload.toString()); const data = this.tryJsonParse(payload.toString()); - this.writeBufferJSON('bridge-groups', payload); + if (this.log.logLevel === LogLevel.DEBUG) this.writeBufferJSON('bridge-groups', payload); this.emit('bridge-groups', data); let index = 1; for (const group of groups) { @@ -834,13 +835,19 @@ export class Zigbee2MQTT extends EventEmitter { } return; } - /* - if (entity.includes('_-_')) { - // Eve app test mode! - const foundDevice = this.z2mDevices.find((device) => device.friendly_name.includes(entity)); - entity = foundDevice ? foundDevice.friendly_name : entity; + + // Log the first 1000 payloads + if (this.log.logLevel === LogLevel.DEBUG && this.loggedEntries < 1000) { + const logEntry = { + entity, + service, + payload: payload.toString(), + }; + const filePath = path.join(this.mqttDataPath, 'bridge-payloads.txt'); + fs.appendFileSync(filePath, JSON.stringify(logEntry) + '\n'); + this.loggedEntries++; } - */ + const foundDevice = this.z2mDevices.findIndex((device) => device.ieee_address === entity || device.friendly_name === entity); if (foundDevice !== -1) { this.handleDeviceMessage(foundDevice, entity, service, payload); @@ -1015,13 +1022,13 @@ export class Zigbee2MQTT extends EventEmitter { const seconds = Math.floor((difference % (1000 * 60)) / 1000); return `${seconds} seconds ago`; }; - this.writeBufferJSON('networkmap_' + data.data.type, payload); + if (this.log.logLevel === LogLevel.DEBUG) this.writeBufferJSON('networkmap_' + data.data.type, payload); if (data.data.type === 'graphviz') { - this.writeFile('networkmap_' + data.data.type + '.txt', data.data.value); + if (this.log.logLevel === LogLevel.DEBUG) this.writeFile('networkmap_' + data.data.type + '.txt', data.data.value); } if (data.data.type === 'plantuml') { - this.writeFile('networkmap_' + data.data.type + '.txt', data.data.value); + if (this.log.logLevel === LogLevel.DEBUG) this.writeFile('networkmap_' + data.data.type + '.txt', data.data.value); } if (data.data.type === 'raw') { // Log nodes with links diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json deleted file mode 100644 index 3e1c3ab..0000000 --- a/tsconfig.eslint.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "compilerOptions": { - "incremental": true, - "composite": true, - "target": "esnext", - "lib": ["ESNext"], - "module": "nodenext", - "rootDir": "./src", - "moduleResolution": "nodenext", - "resolveJsonModule": true, - "allowJs": true, - "checkJs": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "outDir": "./dist", - "preserveConstEnums": true, - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitAny": true, - "alwaysStrict": true, - "noImplicitOverride": true, - "skipLibCheck": true - } -} From 48d2c73a41f8595cd16b1f3ea90c6332b56188b2 Mon Sep 17 00:00:00 2001 From: Luligu Date: Sat, 23 Nov 2024 19:40:44 +0100 Subject: [PATCH 10/17] Dev 2.2.2-dev.5 --- src/platform.test.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/platform.test.ts b/src/platform.test.ts index b20017e..bfe578d 100644 --- a/src/platform.test.ts +++ b/src/platform.test.ts @@ -1,16 +1,18 @@ /* eslint-disable no-console */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unused-vars */ + +import { jest } from '@jest/globals'; + import { Matterbridge, PlatformConfig } from 'matterbridge'; import { AnsiLogger, idn, ign, LogLevel, rs, TimestampFormat, wr } from 'matterbridge/logger'; +import { wait } from 'matterbridge/utils'; + import { ZigbeePlatform } from './platform'; -import { jest } from '@jest/globals'; +import { Zigbee2MQTT } from './zigbee2mqtt'; import { BridgeDevice, BridgeGroup, BridgeInfo } from './zigbee2mqttTypes'; import path from 'path'; import fs from 'fs'; -import { Zigbee2MQTT } from './zigbee2mqtt'; -import exp from 'constants'; -import { wait } from '../../matterbridge/dist/utils/utils'; describe('TestPlatform', () => { let mockMatterbridge: Matterbridge; From eae088854fff4361fae32fc99baed1860173a5e1 Mon Sep 17 00:00:00 2001 From: Luligu Date: Sat, 23 Nov 2024 19:48:43 +0100 Subject: [PATCH 11/17] Dev 2.2.2-dev.5 --- src/platform.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/platform.ts b/src/platform.ts index e265d78..172421f 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -171,6 +171,7 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { }); this.z2m.on('bridge-info', async (bridgeInfo: BridgeInfo) => { + if (bridgeInfo === null || bridgeInfo === undefined) return; this.z2mBridgeInfo = bridgeInfo; this.log.info(`zigbee2MQTT version ${this.z2mBridgeInfo.version} zh version ${this.z2mBridgeInfo.zigbee_herdsman.version} zhc version ${this.z2mBridgeInfo.zigbee_herdsman_converters.version}`); if (this.z2mBridgeInfo.config.advanced.output === 'attribute') this.log.error(`zigbee2MQTT advanced.output must be 'json' or 'attribute_and_json'. Now is ${this.z2mBridgeInfo.config.advanced.output}`); @@ -179,6 +180,7 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { }); this.z2m.on('bridge-devices', async (devices: BridgeDevice[]) => { + if (devices === null || devices === undefined) return; this.log.info(`zigbee2MQTT sent ${devices.length} devices ${this.z2mDevicesRegistered ? 'already registered' : ''}`); if (config.injectDevices) { this.log.warn(`***Injecting virtual devices from ${path.join(matterbridge.matterbridgeDirectory, config.injectDevices as string)}`); @@ -206,6 +208,7 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { }); this.z2m.on('bridge-groups', async (groups: BridgeGroup[]) => { + if (groups === null || groups === undefined) return; this.log.info(`zigbee2MQTT sent ${groups.length} groups ${this.z2mGroupsRegistered ? 'already registered' : ''}`); this.z2mBridgeGroups = groups; From c7accafeec69600f52c82ae5a9c3f38fd0f979ad Mon Sep 17 00:00:00 2001 From: Luligu Date: Sat, 23 Nov 2024 19:51:32 +0100 Subject: [PATCH 12/17] Dev 2.2.2-dev.5 --- .gitignore | 3 - .npmignore | 1 + src/mock/bridge-devices.json | 7604 ++++++++++++++++++++++++++++++++++ src/mock/bridge-groups.json | 123 + src/mock/bridge-info.json | 1417 +++++++ 5 files changed, 9145 insertions(+), 3 deletions(-) create mode 100644 src/mock/bridge-devices.json create mode 100644 src/mock/bridge-groups.json create mode 100644 src/mock/bridge-info.json diff --git a/.gitignore b/.gitignore index 5739bac..416c2a7 100644 --- a/.gitignore +++ b/.gitignore @@ -44,7 +44,4 @@ replay_pid* # zigbee2mqtt temp/ -bridge-info.json -bridge-devices.json -bridge-groups.json TODO.md \ No newline at end of file diff --git a/.npmignore b/.npmignore index 485b4eb..053d772 100644 --- a/.npmignore +++ b/.npmignore @@ -163,5 +163,6 @@ screenshot # other stuff temp/ +mock/ TODO.md yellow-button.png \ No newline at end of file diff --git a/src/mock/bridge-devices.json b/src/mock/bridge-devices.json new file mode 100644 index 0000000..fd325dd --- /dev/null +++ b/src/mock/bridge-devices.json @@ -0,0 +1,7604 @@ +[ + { + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + }, + "2": { + "bindings": [], + "clusters": { + "input": [], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + }, + "3": { + "bindings": [], + "clusters": { + "input": [], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + }, + "4": { + "bindings": [], + "clusters": { + "input": [], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + }, + "5": { + "bindings": [], + "clusters": { + "input": [], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + }, + "6": { + "bindings": [], + "clusters": { + "input": [], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + }, + "8": { + "bindings": [], + "clusters": { + "input": [], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + }, + "10": { + "bindings": [], + "clusters": { + "input": [], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + }, + "11": { + "bindings": [], + "clusters": { + "input": [ + "ssIasAce", + "genTime" + ], + "output": [ + "ssIasZone", + "ssIasWd" + ] + }, + "configured_reportings": [], + "scenes": [] + }, + "12": { + "bindings": [], + "clusters": { + "input": [], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + }, + "13": { + "bindings": [], + "clusters": { + "input": [ + "genOta" + ], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + }, + "47": { + "bindings": [], + "clusters": { + "input": [], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + }, + "110": { + "bindings": [], + "clusters": { + "input": [], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + }, + "242": { + "bindings": [], + "clusters": { + "input": [], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Coordinator", + "ieee_address": "0x00124b0025e1f196", + "interview_completed": true, + "interviewing": false, + "network_address": 0, + "supported": true, + "type": "Coordinator" + }, + { + "date_code": "20191205", + "definition": { + "description": "Temperature and humidity sensor", + "exposes": [ + { + "access": 1, + "category": "diagnostic", + "description": "Remaining battery in %, can take up to 24 hours before reported", + "label": "Battery", + "name": "battery", + "property": "battery", + "type": "numeric", + "unit": "%", + "value_max": 100, + "value_min": 0 + }, + { + "access": 1, + "description": "Measured temperature value", + "label": "Temperature", + "name": "temperature", + "property": "temperature", + "type": "numeric", + "unit": "°C" + }, + { + "access": 1, + "description": "Measured relative humidity", + "label": "Humidity", + "name": "humidity", + "property": "humidity", + "type": "numeric", + "unit": "%" + }, + { + "access": 1, + "description": "The measured atmospheric pressure", + "label": "Pressure", + "name": "pressure", + "property": "pressure", + "type": "numeric", + "unit": "hPa" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Voltage of the battery in millivolts", + "label": "Voltage", + "name": "voltage", + "property": "voltage", + "type": "numeric", + "unit": "mV" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "WSDCGQ11LM", + "options": [ + { + "access": 2, + "description": "Calibrates the temperature value (absolute offset), takes into effect on next report of device.", + "label": "Temperature calibration", + "name": "temperature_calibration", + "property": "temperature_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for temperature, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Temperature precision", + "name": "temperature_precision", + "property": "temperature_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the humidity value (absolute offset), takes into effect on next report of device.", + "label": "Humidity calibration", + "name": "humidity_calibration", + "property": "humidity_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for humidity, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Humidity precision", + "name": "humidity_precision", + "property": "humidity_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the pressure value (absolute offset), takes into effect on next report of device.", + "label": "Pressure calibration", + "name": "pressure_calibration", + "property": "pressure_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for pressure, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Pressure precision", + "name": "pressure_precision", + "property": "pressure_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + } + ], + "supports_ota": false, + "vendor": "Aqara" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genBasic", + "genIdentify", + "65535", + "msTemperatureMeasurement", + "msPressureMeasurement", + "msRelativeHumidity" + ], + "output": [ + "genBasic", + "genGroups", + "65535" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Climate sensor", + "ieee_address": "0x00158d000945f2b1", + "interview_completed": true, + "interviewing": false, + "manufacturer": "LUMI", + "model_id": "lumi.weather", + "network_address": 45159, + "power_source": "Battery", + "software_build_id": "3000-0001", + "supported": true, + "type": "EndDevice" + }, + { + "date_code": "20161128", + "definition": { + "description": "Door and window sensor", + "exposes": [ + { + "access": 1, + "category": "diagnostic", + "description": "Remaining battery in %, can take up to 24 hours before reported", + "label": "Battery", + "name": "battery", + "property": "battery", + "type": "numeric", + "unit": "%", + "value_max": 100, + "value_min": 0 + }, + { + "access": 1, + "description": "Indicates if the contact is closed (= true) or open (= false)", + "label": "Contact", + "name": "contact", + "property": "contact", + "type": "binary", + "value_off": true, + "value_on": false + }, + { + "access": 1, + "category": "diagnostic", + "description": "Temperature of the device", + "label": "Device temperature", + "name": "device_temperature", + "property": "device_temperature", + "type": "numeric", + "unit": "°C" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Voltage of the battery in millivolts", + "label": "Voltage", + "name": "voltage", + "property": "voltage", + "type": "numeric", + "unit": "mV" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Number of power outages", + "label": "Power outage count", + "name": "power_outage_count", + "property": "power_outage_count", + "type": "numeric" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Indicates how many times the sensor was triggered (since last scheduled report)", + "label": "Trigger count", + "name": "trigger_count", + "property": "trigger_count", + "type": "numeric" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "MCCGQ11LM", + "options": [ + { + "access": 2, + "description": "Calibrates the device_temperature value (absolute offset), takes into effect on next report of device.", + "label": "Device temperature calibration", + "name": "device_temperature_calibration", + "property": "device_temperature_calibration", + "type": "numeric" + } + ], + "supports_ota": false, + "vendor": "Aqara" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genBasic", + "genIdentify", + "65535", + "genOnOff" + ], + "output": [ + "genBasic", + "genGroups", + "65535" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Contact sensor", + "ieee_address": "0x00158d00091e7bd6", + "interview_completed": true, + "interviewing": false, + "manufacturer": "LUMI", + "model_id": "lumi.sensor_magnet.aq2", + "network_address": 51920, + "power_source": "Battery", + "software_build_id": "3000-0001", + "supported": true, + "type": "EndDevice" + }, + { + "date_code": "20200312", + "definition": { + "description": "Light sensor T1", + "exposes": [ + { + "access": 1, + "category": "diagnostic", + "description": "Remaining battery in %, can take up to 24 hours before reported", + "label": "Battery", + "name": "battery", + "property": "battery", + "type": "numeric", + "unit": "%", + "value_max": 100, + "value_min": 0 + }, + { + "access": 1, + "category": "diagnostic", + "description": "Voltage of the battery in millivolts", + "label": "Voltage", + "name": "voltage", + "property": "voltage", + "type": "numeric", + "unit": "mV" + }, + { + "access": 1, + "description": "Raw measured illuminance", + "label": "Illuminance", + "name": "illuminance", + "property": "illuminance", + "type": "numeric" + }, + { + "access": 1, + "description": "Measured illuminance in lux", + "label": "Illuminance (lux)", + "name": "illuminance_lux", + "property": "illuminance_lux", + "type": "numeric", + "unit": "lx" + }, + { + "access": 7, + "description": "Time interval in seconds to report after light changes", + "label": "Detection period", + "name": "detection_period", + "property": "detection_period", + "type": "numeric", + "unit": "s", + "value_max": 59, + "value_min": 1 + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "GZCGQ11LM", + "options": [ + { + "access": 2, + "description": "Calibrates the illuminance value (percentual offset), takes into effect on next report of device.", + "label": "Illuminance calibration", + "name": "illuminance_calibration", + "property": "illuminance_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Calibrates the illuminance_lux value (percentual offset), takes into effect on next report of device.", + "label": "Illuminance lux calibration", + "name": "illuminance_lux_calibration", + "property": "illuminance_lux_calibration", + "type": "numeric" + } + ], + "supports_ota": true, + "vendor": "Aqara" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genBasic", + "msIlluminanceMeasurement", + "genIdentify", + "genPowerCfg" + ], + "output": [ + "genIdentify", + "genOta" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Light sensor", + "ieee_address": "0x54ef44100085c321", + "interview_completed": true, + "interviewing": false, + "manufacturer": "LUMI", + "model_id": "lumi.sen_ill.agl01", + "network_address": 13361, + "power_source": "Battery", + "software_build_id": "2019\u0000www.", + "supported": true, + "type": "EndDevice" + }, + { + "date_code": "20210125", + "definition": { + "description": "Motion sensor", + "exposes": [ + { + "access": 5, + "category": "diagnostic", + "description": "Remaining battery in %", + "label": "Battery", + "name": "battery", + "property": "battery", + "type": "numeric", + "unit": "%", + "value_max": 100, + "value_min": 0 + }, + { + "access": 5, + "category": "diagnostic", + "description": "Reported battery voltage in millivolts", + "label": "Voltage", + "name": "voltage", + "property": "voltage", + "type": "numeric", + "unit": "mV" + }, + { + "access": 1, + "description": "Indicates whether the device detected occupancy", + "label": "Occupancy", + "name": "occupancy", + "property": "occupancy", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 1, + "category": "diagnostic", + "description": "Indicates whether the battery of the device is almost empty", + "label": "Battery low", + "name": "battery_low", + "property": "battery_low", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "SNZB-03", + "options": [], + "supports_ota": false, + "vendor": "SONOFF" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [ + { + "cluster": "genPowerCfg", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + } + ], + "clusters": { + "input": [ + "genBasic", + "genIdentify", + "ssIasZone", + "genPowerCfg" + ], + "output": [ + "genIdentify" + ] + }, + "configured_reportings": [ + { + "attribute": "batteryVoltage", + "cluster": "genPowerCfg", + "maximum_report_interval": 7200, + "minimum_report_interval": 3600, + "reportable_change": 0 + }, + { + "attribute": "batteryPercentageRemaining", + "cluster": "genPowerCfg", + "maximum_report_interval": 7200, + "minimum_report_interval": 3600, + "reportable_change": 0 + } + ], + "scenes": [] + } + }, + "friendly_name": "Motion sensor", + "ieee_address": "0x00124b0023b86d16", + "interview_completed": true, + "interviewing": false, + "manufacturer": "eWeLink", + "model_id": "MS01", + "network_address": 50956, + "power_source": "Battery", + "supported": true, + "type": "EndDevice" + }, + { + "date_code": "20170627", + "definition": { + "description": "Motion sensor", + "exposes": [ + { + "access": 1, + "category": "diagnostic", + "description": "Remaining battery in %, can take up to 24 hours before reported", + "label": "Battery", + "name": "battery", + "property": "battery", + "type": "numeric", + "unit": "%", + "value_max": 100, + "value_min": 0 + }, + { + "access": 1, + "description": "Indicates whether the device detected occupancy", + "label": "Occupancy", + "name": "occupancy", + "property": "occupancy", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 1, + "category": "diagnostic", + "description": "Temperature of the device", + "label": "Device temperature", + "name": "device_temperature", + "property": "device_temperature", + "type": "numeric", + "unit": "°C" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Voltage of the battery in millivolts", + "label": "Voltage", + "name": "voltage", + "property": "voltage", + "type": "numeric", + "unit": "mV" + }, + { + "access": 1, + "description": "Measured illuminance in lux", + "label": "Illuminance (lux)", + "name": "illuminance_lux", + "property": "illuminance", + "type": "numeric", + "unit": "lx" + }, + { + "access": 1, + "description": "Measured illuminance in lux", + "label": "Illuminance", + "name": "illuminance", + "property": "illuminance", + "type": "numeric", + "unit": "lx" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Number of power outages", + "label": "Power outage count", + "name": "power_outage_count", + "property": "power_outage_count", + "type": "numeric" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "RTCGQ11LM", + "options": [ + { + "access": 2, + "description": "Calibrates the device_temperature value (absolute offset), takes into effect on next report of device.", + "label": "Device temperature calibration", + "name": "device_temperature_calibration", + "property": "device_temperature_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Calibrates the illuminance_lux value (percentual offset), takes into effect on next report of device.", + "label": "Illuminance lux calibration", + "name": "illuminance_lux_calibration", + "property": "illuminance_lux_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Calibrates the illuminance value (percentual offset), takes into effect on next report of device.", + "label": "Illuminance calibration", + "name": "illuminance_calibration", + "property": "illuminance_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Time in seconds after which occupancy is cleared after detecting it (default 90 seconds).", + "label": "Occupancy timeout", + "name": "occupancy_timeout", + "property": "occupancy_timeout", + "type": "numeric", + "value_min": 0 + }, + { + "access": 2, + "description": "Sends a message the last time occupancy (occupancy: true) was detected. When setting this for example to [10, 60] a `{\"no_occupancy_since\": 10}` will be send after 10 seconds and a `{\"no_occupancy_since\": 60}` after 60 seconds.", + "item_type": { + "access": 3, + "label": "Time", + "name": "time", + "type": "numeric" + }, + "label": "No occupancy since", + "name": "no_occupancy_since", + "property": "no_occupancy_since", + "type": "list" + } + ], + "supports_ota": false, + "vendor": "Aqara" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genBasic", + "65535", + "msOccupancySensing", + "msIlluminanceMeasurement", + "ssIasZone", + "genPowerCfg", + "genIdentify" + ], + "output": [ + "genBasic", + "genOta" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Occupancy sensor", + "ieee_address": "0x00158d0007e778c1", + "interview_completed": true, + "interviewing": false, + "manufacturer": "LUMI", + "model_id": "lumi.sensor_motion.aq2", + "network_address": 6742, + "power_source": "Battery", + "software_build_id": "3000-0001", + "supported": true, + "type": "EndDevice" + }, + { + "date_code": "Jan 4 2023", + "definition": { + "description": "Motion sensor P1", + "exposes": [ + { + "access": 1, + "description": "Indicates whether the device detected occupancy", + "label": "Occupancy", + "name": "occupancy", + "property": "occupancy", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 1, + "description": "Measured illuminance in lux", + "label": "Illuminance (lux)", + "name": "illuminance_lux", + "property": "illuminance", + "type": "numeric", + "unit": "lx" + }, + { + "access": 1, + "description": "Measured illuminance in lux", + "label": "Illuminance", + "name": "illuminance", + "property": "illuminance", + "type": "numeric", + "unit": "lx" + }, + { + "access": 7, + "category": "config", + "description": "Select motion sensitivity to use. Press pairing button right before changing this otherwise it will fail.", + "label": "Motion sensitivity", + "name": "motion_sensitivity", + "property": "motion_sensitivity", + "type": "enum", + "values": [ + "low", + "medium", + "high" + ] + }, + { + "access": 7, + "category": "config", + "description": "Time interval between action detection. Press pairing button right before changing this otherwise it will fail.", + "label": "Detection interval", + "name": "detection_interval", + "property": "detection_interval", + "type": "numeric", + "unit": "s", + "value_max": 65535, + "value_min": 2 + }, + { + "access": 7, + "category": "config", + "description": "When this option is enabled then blue LED will blink once when motion is detected. Press pairing button right before changing this otherwise it will fail.", + "label": "Trigger indicator", + "name": "trigger_indicator", + "property": "trigger_indicator", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 1, + "category": "diagnostic", + "description": "Temperature of the device", + "label": "Device temperature", + "name": "device_temperature", + "property": "device_temperature", + "type": "numeric", + "unit": "°C" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Remaining battery in %, can take up to 24 hours before reported", + "label": "Battery", + "name": "battery", + "property": "battery", + "type": "numeric", + "unit": "%", + "value_max": 100, + "value_min": 0 + }, + { + "access": 1, + "category": "diagnostic", + "description": "Voltage of the battery in millivolts", + "label": "Voltage", + "name": "voltage", + "property": "voltage", + "type": "numeric", + "unit": "mV" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "RTCGQ14LM", + "options": [ + { + "access": 2, + "description": "Calibrates the illuminance_lux value (percentual offset), takes into effect on next report of device.", + "label": "Illuminance lux calibration", + "name": "illuminance_lux_calibration", + "property": "illuminance_lux_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Calibrates the illuminance value (percentual offset), takes into effect on next report of device.", + "label": "Illuminance calibration", + "name": "illuminance_calibration", + "property": "illuminance_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Calibrates the device_temperature value (absolute offset), takes into effect on next report of device.", + "label": "Device temperature calibration", + "name": "device_temperature_calibration", + "property": "device_temperature_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Time in seconds after which occupancy is cleared after detecting it (default is \"detection_interval\" + 2 seconds). The value must be equal to or greater than \"detection_interval\", and it can also be a fraction.", + "label": "Occupancy timeout", + "name": "occupancy_timeout", + "property": "occupancy_timeout", + "type": "numeric", + "unit": "s", + "value_min": 0, + "value_step": 0.1 + }, + { + "access": 2, + "description": "Sends a message the last time occupancy (occupancy: true) was detected. When setting this for example to [10, 60] a `{\"no_occupancy_since\": 10}` will be send after 10 seconds and a `{\"no_occupancy_since\": 60}` after 60 seconds.", + "item_type": { + "access": 3, + "label": "Time", + "name": "time", + "type": "numeric" + }, + "label": "No occupancy since", + "name": "no_occupancy_since", + "property": "no_occupancy_since", + "type": "list" + } + ], + "supports_ota": true, + "vendor": "Aqara" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genBasic", + "genPowerCfg", + "genIdentify", + "manuSpecificLumi" + ], + "output": [ + "genIdentify", + "genOta", + "manuSpecificLumi" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Occupancy sensor P1", + "ieee_address": "0x54ef44100072a2ea", + "interview_completed": true, + "interviewing": false, + "manufacturer": "LUMI", + "model_id": "lumi.motion.ac02", + "network_address": 53399, + "power_source": "Battery", + "software_build_id": "0.0.0_0010", + "supported": true, + "type": "EndDevice" + }, + { + "date_code": "Dec 29 2021", + "definition": { + "description": "Single switch module T1 (with neutral), CN", + "exposes": [ + { + "features": [ + { + "access": 7, + "description": "On/off state of the switch", + "label": "State", + "name": "state", + "property": "state", + "type": "binary", + "value_off": "OFF", + "value_on": "ON", + "value_toggle": "TOGGLE" + } + ], + "type": "switch" + }, + { + "access": 5, + "category": "diagnostic", + "description": "Instantaneous measured power", + "label": "Power", + "name": "power", + "property": "power", + "type": "numeric", + "unit": "W" + }, + { + "access": 1, + "description": "Sum of consumed energy", + "label": "Energy", + "name": "energy", + "property": "energy", + "type": "numeric", + "unit": "kWh" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Temperature of the device", + "label": "Device temperature", + "name": "device_temperature", + "property": "device_temperature", + "type": "numeric", + "unit": "°C" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Measured electrical potential value", + "label": "Voltage", + "name": "voltage", + "property": "voltage", + "type": "numeric", + "unit": "V" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Instantaneous measured electrical current", + "label": "Current", + "name": "current", + "property": "current", + "type": "numeric", + "unit": "A" + }, + { + "access": 7, + "category": "config", + "description": "Enable/disable the power outage memory, this recovers the on/off mode after power failure", + "label": "Power outage memory", + "name": "power_outage_memory", + "property": "power_outage_memory", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 7, + "category": "config", + "description": "Enable/disable the LED at night", + "label": "LED disabled night", + "name": "led_disabled_night", + "property": "led_disabled_night", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 7, + "description": "Wall switch type", + "label": "Switch type", + "name": "switch_type", + "property": "switch_type", + "type": "enum", + "values": [ + "toggle", + "momentary" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "DLKZMK11LM", + "options": [ + { + "access": 2, + "description": "Calibrates the power value (percentual offset), takes into effect on next report of device.", + "label": "Power calibration", + "name": "power_calibration", + "property": "power_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for power, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Power precision", + "name": "power_precision", + "property": "power_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the energy value (percentual offset), takes into effect on next report of device.", + "label": "Energy calibration", + "name": "energy_calibration", + "property": "energy_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for energy, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Energy precision", + "name": "energy_precision", + "property": "energy_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the device_temperature value (absolute offset), takes into effect on next report of device.", + "label": "Device temperature calibration", + "name": "device_temperature_calibration", + "property": "device_temperature_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Calibrates the voltage value (percentual offset), takes into effect on next report of device.", + "label": "Voltage calibration", + "name": "voltage_calibration", + "property": "voltage_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for voltage, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Voltage precision", + "name": "voltage_precision", + "property": "voltage_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the current value (percentual offset), takes into effect on next report of device.", + "label": "Current calibration", + "name": "current_calibration", + "property": "current_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for current, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Current precision", + "name": "current_precision", + "property": "current_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "State actions will also be published as 'action' when true (default false).", + "label": "State action", + "name": "state_action", + "property": "state_action", + "type": "binary", + "value_off": false, + "value_on": true + } + ], + "supports_ota": true, + "vendor": "Aqara" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genBasic", + "genDeviceTempCfg", + "genIdentify", + "genGroups", + "genScenes", + "genAlarms", + "genTime", + "manuSpecificLumi" + ], + "output": [ + "genTime", + "genOta", + "65535" + ] + }, + "configured_reportings": [], + "scenes": [ + { + "id": 0, + "name": "OFF" + }, + { + "id": 1, + "name": "ON" + } + ] + }, + "21": { + "bindings": [], + "clusters": { + "input": [ + "genAnalogInput" + ], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + }, + "31": { + "bindings": [], + "clusters": { + "input": [ + "genAnalogInput" + ], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + }, + "41": { + "bindings": [], + "clusters": { + "input": [ + "genMultistateInput" + ], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + }, + "242": { + "bindings": [], + "clusters": { + "input": [], + "output": [ + "greenPower" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Aqara switch T1", + "ieee_address": "0x54ef44100086d51f", + "interview_completed": true, + "interviewing": false, + "manufacturer": "LUMI", + "model_id": "lumi.switch.n0acn2", + "network_address": 982, + "power_source": "Mains (single phase)", + "software_build_id": "", + "supported": true, + "type": "Router" + }, + { + "date_code": "20240110", + "definition": { + "description": "Dual relay module T2", + "exposes": [ + { + "endpoint": "l1", + "features": [ + { + "access": 7, + "description": "On/off state of the switch", + "endpoint": "l1", + "label": "State", + "name": "state", + "property": "state_l1", + "type": "binary", + "value_off": "OFF", + "value_on": "ON", + "value_toggle": "TOGGLE" + } + ], + "type": "switch" + }, + { + "endpoint": "l2", + "features": [ + { + "access": 7, + "description": "On/off state of the switch", + "endpoint": "l2", + "label": "State", + "name": "state", + "property": "state_l2", + "type": "binary", + "value_off": "OFF", + "value_on": "ON", + "value_toggle": "TOGGLE" + } + ], + "type": "switch" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Instantaneous measured power", + "label": "Power", + "name": "power", + "property": "power", + "type": "numeric", + "unit": "W" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Instantaneous measured electrical current", + "label": "Current", + "name": "current", + "property": "current", + "type": "numeric", + "unit": "A" + }, + { + "access": 1, + "description": "Sum of consumed energy", + "label": "Energy", + "name": "energy", + "property": "energy", + "type": "numeric", + "unit": "kWh" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Measured electrical potential value", + "label": "Voltage", + "name": "voltage", + "property": "voltage", + "type": "numeric", + "unit": "V" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Temperature of the device", + "label": "Device temperature", + "name": "device_temperature", + "property": "device_temperature", + "type": "numeric", + "unit": "°C" + }, + { + "access": 7, + "category": "config", + "description": "External switch type", + "label": "Switch type", + "name": "switch_type", + "property": "switch_type", + "type": "enum", + "values": [ + "toggle", + "momentary", + "none" + ] + }, + { + "access": 7, + "category": "config", + "description": "Controls the behavior when the device is powered on after power loss", + "label": "Power on behavior", + "name": "power_on_behavior", + "property": "power_on_behavior", + "type": "enum", + "values": [ + "on", + "previous", + "off", + "toggle" + ] + }, + { + "access": 7, + "category": "config", + "description": "Decoupled mode for 1st relay", + "endpoint": "l1", + "label": "Operation mode", + "name": "operation_mode", + "property": "operation_mode_l1", + "type": "enum", + "values": [ + "decoupled", + "control_relay" + ] + }, + { + "access": 7, + "category": "config", + "description": "Decoupled mode for 2nd relay", + "endpoint": "l2", + "label": "Operation mode", + "name": "operation_mode", + "property": "operation_mode_l2", + "type": "enum", + "values": [ + "decoupled", + "control_relay" + ] + }, + { + "access": 7, + "description": "Enabling prevents both relays being on at the same time (Interlock)", + "label": "Interlock", + "name": "interlock", + "property": "interlock", + "type": "binary", + "value_off": "OFF", + "value_on": "ON" + }, + { + "access": 7, + "description": "Work mode: Power mode, Dry mode with impulse, Dry mode", + "label": "Mode", + "name": "mode", + "property": "mode", + "type": "enum", + "values": [ + "power", + "pulse", + "dry" + ] + }, + { + "access": 7, + "description": "Impulse length in Dry mode with impulse", + "label": "Pulse length", + "name": "pulse_length", + "property": "pulse_length", + "type": "numeric", + "unit": "ms", + "value_max": 2000, + "value_min": 200 + }, + { + "access": 1, + "category": "diagnostic", + "description": "Triggered action (e.g. a button click)", + "label": "Action", + "name": "action", + "property": "action", + "type": "enum", + "values": [ + "single_l1", + "single_l2" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "LLKZMK12LM", + "options": [ + { + "access": 2, + "description": "Calibrates the power value (percentual offset), takes into effect on next report of device.", + "label": "Power calibration", + "name": "power_calibration", + "property": "power_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for power, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Power precision", + "name": "power_precision", + "property": "power_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the current value (percentual offset), takes into effect on next report of device.", + "label": "Current calibration", + "name": "current_calibration", + "property": "current_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for current, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Current precision", + "name": "current_precision", + "property": "current_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the energy value (percentual offset), takes into effect on next report of device.", + "label": "Energy calibration", + "name": "energy_calibration", + "property": "energy_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for energy, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Energy precision", + "name": "energy_precision", + "property": "energy_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the voltage value (percentual offset), takes into effect on next report of device.", + "label": "Voltage calibration", + "name": "voltage_calibration", + "property": "voltage_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for voltage, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Voltage precision", + "name": "voltage_precision", + "property": "voltage_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the device_temperature value (absolute offset), takes into effect on next report of device.", + "label": "Device temperature calibration", + "name": "device_temperature_calibration", + "property": "device_temperature_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "State actions will also be published as 'action' when true (default false).", + "label": "State action", + "name": "state_action", + "property": "state_action", + "type": "binary", + "value_off": false, + "value_on": true + } + ], + "supports_ota": true, + "vendor": "Aqara" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [ + { + "cluster": "genOnOff", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + } + ], + "clusters": { + "input": [ + "haElectricalMeasurement", + "seMetering", + "genScenes", + "genGroups", + "genIdentify", + "genMultistateInput", + "genBasic", + "genOnOff", + "manuSpecificLumi" + ], + "output": [ + "genOta", + "genTime" + ] + }, + "configured_reportings": [ + { + "attribute": "onOff", + "cluster": "genOnOff", + "maximum_report_interval": 3600, + "minimum_report_interval": 0, + "reportable_change": 0 + } + ], + "scenes": [] + }, + "2": { + "bindings": [ + { + "cluster": "genOnOff", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + } + ], + "clusters": { + "input": [ + "genScenes", + "genGroups", + "genIdentify", + "genMultistateInput", + "genOnOff", + "manuSpecificLumi" + ], + "output": [] + }, + "configured_reportings": [ + { + "attribute": "onOff", + "cluster": "genOnOff", + "maximum_report_interval": 3600, + "minimum_report_interval": 0, + "reportable_change": 0 + } + ], + "scenes": [] + }, + "21": { + "bindings": [], + "clusters": { + "input": [ + "genAnalogInput" + ], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Aqara Dual relay module T2", + "ieee_address": "0x54ef4410009716a8", + "interview_completed": true, + "interviewing": false, + "manufacturer": "Aqara", + "model_id": "lumi.switch.acn047", + "network_address": 15221, + "power_source": "DC Source", + "supported": true, + "type": "Router" + }, + { + "date_code": "", + "definition": { + "description": "Zigbee + RF curtain switch module", + "exposes": [ + { + "features": [ + { + "access": 3, + "label": "State", + "name": "state", + "property": "state", + "type": "enum", + "values": [ + "OPEN", + "CLOSE", + "STOP" + ] + }, + { + "access": 7, + "description": "Position of this cover", + "label": "Position", + "name": "position", + "property": "position", + "type": "numeric", + "unit": "%", + "value_max": 100, + "value_min": 0 + } + ], + "type": "cover" + }, + { + "access": 7, + "label": "Calibration time", + "name": "calibration_time", + "property": "calibration_time", + "type": "numeric", + "value_max": 100, + "value_min": 0 + }, + { + "access": 1, + "label": "Moving", + "name": "moving", + "property": "moving", + "type": "enum", + "values": [ + "UP", + "STOP", + "DOWN" + ] + }, + { + "access": 7, + "label": "Motor reversal", + "name": "motor_reversal", + "property": "motor_reversal", + "type": "binary", + "value_off": "OFF", + "value_on": "ON" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "MS-108ZR", + "options": [ + { + "access": 2, + "description": "Inverts the cover position, false: open=100,close=0, true: open=0,close=100 (default false).", + "label": "Invert cover", + "name": "invert_cover", + "property": "invert_cover", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 2, + "description": "Do not publish set cover target position as a normal 'position' value (default false).", + "label": "Cover position tilt disable report", + "name": "cover_position_tilt_disable_report", + "property": "cover_position_tilt_disable_report", + "type": "binary", + "value_off": false, + "value_on": true + } + ], + "supports_ota": true, + "vendor": "Moes" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [ + { + "cluster": "closuresWindowCovering", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + } + ], + "clusters": { + "input": [ + "genBasic", + "genGroups", + "genScenes", + "closuresWindowCovering", + "genOnOff" + ], + "output": [ + "genOta", + "genTime" + ] + }, + "configured_reportings": [ + { + "attribute": "currentPositionLiftPercentage", + "cluster": "closuresWindowCovering", + "maximum_report_interval": 3600, + "minimum_report_interval": 60, + "reportable_change": 0 + } + ], + "scenes": [] + } + }, + "friendly_name": "Window shutter", + "ieee_address": "0x70ac08fffee6dd5e", + "interview_completed": true, + "interviewing": false, + "manufacturer": "_TZ3000_1dd0d5yi", + "model_id": "TS130F", + "network_address": 63434, + "power_source": "Mains (single phase)", + "supported": true, + "type": "Router" + }, + { + "date_code": "20180525", + "definition": { + "description": "Wireless mini switch", + "exposes": [ + { + "access": 1, + "category": "diagnostic", + "description": "Remaining battery in %, can take up to 24 hours before reported", + "label": "Battery", + "name": "battery", + "property": "battery", + "type": "numeric", + "unit": "%", + "value_max": 100, + "value_min": 0 + }, + { + "access": 1, + "category": "diagnostic", + "description": "Voltage of the battery in millivolts", + "label": "Voltage", + "name": "voltage", + "property": "voltage", + "type": "numeric", + "unit": "mV" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Temperature of the device", + "label": "Device temperature", + "name": "device_temperature", + "property": "device_temperature", + "type": "numeric", + "unit": "°C" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Number of power outages (since last pairing)", + "label": "Power outage count", + "name": "power_outage_count", + "property": "power_outage_count", + "type": "numeric" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Triggered action (e.g. a button click)", + "label": "Action", + "name": "action", + "property": "action", + "type": "enum", + "values": [ + "single", + "double", + "triple", + "quadruple", + "hold", + "release" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "WXKG11LM", + "options": [ + { + "access": 2, + "description": "Calibrates the device_temperature value (absolute offset), takes into effect on next report of device.", + "label": "Device temperature calibration", + "name": "device_temperature_calibration", + "property": "device_temperature_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Set to false to disable the legacy integration (highly recommended), will change structure of the published payload (default true).", + "label": "Legacy", + "name": "legacy", + "property": "legacy", + "type": "binary", + "value_off": false, + "value_on": true + } + ], + "supports_ota": false, + "vendor": "Aqara" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genBasic", + "genMultistateInput", + "genIdentify" + ], + "output": [ + "genBasic" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Scenes button", + "ieee_address": "0x00158d0008066798", + "interview_completed": true, + "interviewing": false, + "manufacturer": "LUMI", + "model_id": "lumi.remote.b1acn01", + "network_address": 34246, + "power_source": "Battery", + "software_build_id": "3000-0001", + "supported": true, + "type": "EndDevice" + }, + { + "date_code": "", + "definition": { + "description": "Smart heating thermostat", + "exposes": [ + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + }, + { + "features": [ + { + "access": 3, + "description": "Enables/disables physical input on the device", + "label": "State", + "name": "state", + "property": "child_lock", + "type": "binary", + "value_off": "UNLOCK", + "value_on": "LOCK" + } + ], + "label": "Child lock", + "type": "lock" + }, + { + "access": 3, + "description": "The delta between local_temperature and current_heating_setpoint to trigger Heat", + "label": "Deadzone temperature", + "name": "deadzone_temperature", + "property": "deadzone_temperature", + "type": "numeric", + "unit": "°C", + "value_max": 5, + "value_min": 0, + "value_step": 1 + }, + { + "access": 3, + "description": "Maximum temperature limit. Cuts the thermostat out regardless of air temperature if the external floor sensor exceeds this temperature. Only used by the thermostat when in AL sensor mode.", + "label": "Max temperature limit", + "name": "max_temperature_limit", + "property": "max_temperature_limit", + "type": "numeric", + "unit": "°C", + "value_max": 45, + "value_min": 0 + }, + { + "access": 3, + "description": "Minimum temperature limit for frost protection. Turns the thermostat on regardless of setpoint if the temperature drops below this.", + "label": "Min temperature limit", + "name": "min_temperature_limit", + "property": "min_temperature_limit", + "type": "numeric", + "unit": "°C", + "value_max": 5, + "value_min": 1 + }, + { + "features": [ + { + "access": 3, + "description": "Temperature setpoint", + "label": "Current heating setpoint", + "name": "current_heating_setpoint", + "property": "current_heating_setpoint", + "type": "numeric", + "unit": "°C", + "value_max": 45, + "value_min": 5, + "value_step": 1 + }, + { + "access": 1, + "description": "Current temperature measured on the device", + "label": "Local temperature", + "name": "local_temperature", + "property": "local_temperature", + "type": "numeric", + "unit": "°C" + }, + { + "access": 3, + "description": "Offset to add/subtract to the local temperature", + "label": "Local temperature calibration", + "name": "local_temperature_calibration", + "property": "local_temperature_calibration", + "type": "numeric", + "unit": "°C", + "value_max": 30, + "value_min": -30, + "value_step": 0.1 + }, + { + "access": 3, + "description": "Mode of this device", + "label": "System mode", + "name": "system_mode", + "property": "system_mode", + "type": "enum", + "values": [ + "off", + "heat" + ] + }, + { + "access": 1, + "description": "The current running state", + "label": "Running state", + "name": "running_state", + "property": "running_state", + "type": "enum", + "values": [ + "idle", + "heat", + "cool" + ] + }, + { + "access": 3, + "description": "Mode of this device (similar to system_mode)", + "label": "Preset", + "name": "preset", + "property": "preset", + "type": "enum", + "values": [ + "hold", + "program" + ] + } + ], + "type": "climate" + }, + { + "access": 3, + "category": "config", + "description": "Select temperature sensor to use", + "label": "Sensor", + "name": "sensor", + "property": "sensor", + "type": "enum", + "values": [ + "IN", + "AL", + "OU" + ] + }, + { + "access": 3, + "description": "Time of day and setpoint to use when in program mode", + "features": [ + { + "access": 3, + "label": "Weekdays p1 hour", + "name": "weekdays_p1_hour", + "property": "weekdays_p1_hour", + "type": "numeric", + "unit": "h", + "value_max": 23, + "value_min": 0 + }, + { + "access": 3, + "label": "Weekdays p1 minute", + "name": "weekdays_p1_minute", + "property": "weekdays_p1_minute", + "type": "numeric", + "unit": "m", + "value_max": 59, + "value_min": 0 + }, + { + "access": 3, + "label": "Weekdays p1 temperature", + "name": "weekdays_p1_temperature", + "property": "weekdays_p1_temperature", + "type": "numeric", + "unit": "°C", + "value_max": 35, + "value_min": 5, + "value_step": 0.5 + }, + { + "access": 3, + "label": "Weekdays p2 hour", + "name": "weekdays_p2_hour", + "property": "weekdays_p2_hour", + "type": "numeric", + "unit": "h", + "value_max": 23, + "value_min": 0 + }, + { + "access": 3, + "label": "Weekdays p2 minute", + "name": "weekdays_p2_minute", + "property": "weekdays_p2_minute", + "type": "numeric", + "unit": "m", + "value_max": 59, + "value_min": 0 + }, + { + "access": 3, + "label": "Weekdays p2 temperature", + "name": "weekdays_p2_temperature", + "property": "weekdays_p2_temperature", + "type": "numeric", + "unit": "°C", + "value_max": 35, + "value_min": 5, + "value_step": 0.5 + }, + { + "access": 3, + "label": "Weekdays p3 hour", + "name": "weekdays_p3_hour", + "property": "weekdays_p3_hour", + "type": "numeric", + "unit": "h", + "value_max": 23, + "value_min": 0 + }, + { + "access": 3, + "label": "Weekdays p3 minute", + "name": "weekdays_p3_minute", + "property": "weekdays_p3_minute", + "type": "numeric", + "unit": "m", + "value_max": 59, + "value_min": 0 + }, + { + "access": 3, + "label": "Weekdays p3 temperature", + "name": "weekdays_p3_temperature", + "property": "weekdays_p3_temperature", + "type": "numeric", + "unit": "°C", + "value_max": 35, + "value_min": 5, + "value_step": 0.5 + }, + { + "access": 3, + "label": "Weekdays p4 hour", + "name": "weekdays_p4_hour", + "property": "weekdays_p4_hour", + "type": "numeric", + "unit": "h", + "value_max": 23, + "value_min": 0 + }, + { + "access": 3, + "label": "Weekdays p4 minute", + "name": "weekdays_p4_minute", + "property": "weekdays_p4_minute", + "type": "numeric", + "unit": "m", + "value_max": 59, + "value_min": 0 + }, + { + "access": 3, + "label": "Weekdays p4 temperature", + "name": "weekdays_p4_temperature", + "property": "weekdays_p4_temperature", + "type": "numeric", + "unit": "°C", + "value_max": 35, + "value_min": 5, + "value_step": 0.5 + }, + { + "access": 3, + "label": "Saturday p1 hour", + "name": "saturday_p1_hour", + "property": "saturday_p1_hour", + "type": "numeric", + "unit": "h", + "value_max": 23, + "value_min": 0 + }, + { + "access": 3, + "label": "Saturday p1 minute", + "name": "saturday_p1_minute", + "property": "saturday_p1_minute", + "type": "numeric", + "unit": "m", + "value_max": 59, + "value_min": 0 + }, + { + "access": 3, + "label": "Saturday p1 temperature", + "name": "saturday_p1_temperature", + "property": "saturday_p1_temperature", + "type": "numeric", + "unit": "°C", + "value_max": 35, + "value_min": 5, + "value_step": 0.5 + }, + { + "access": 3, + "label": "Saturday p2 hour", + "name": "saturday_p2_hour", + "property": "saturday_p2_hour", + "type": "numeric", + "unit": "h", + "value_max": 23, + "value_min": 0 + }, + { + "access": 3, + "label": "Saturday p2 minute", + "name": "saturday_p2_minute", + "property": "saturday_p2_minute", + "type": "numeric", + "unit": "m", + "value_max": 59, + "value_min": 0 + }, + { + "access": 3, + "label": "Saturday p2 temperature", + "name": "saturday_p2_temperature", + "property": "saturday_p2_temperature", + "type": "numeric", + "unit": "°C", + "value_max": 35, + "value_min": 5, + "value_step": 0.5 + }, + { + "access": 3, + "label": "Saturday p3 hour", + "name": "saturday_p3_hour", + "property": "saturday_p3_hour", + "type": "numeric", + "unit": "h", + "value_max": 23, + "value_min": 0 + }, + { + "access": 3, + "label": "Saturday p3 minute", + "name": "saturday_p3_minute", + "property": "saturday_p3_minute", + "type": "numeric", + "unit": "m", + "value_max": 59, + "value_min": 0 + }, + { + "access": 3, + "label": "Saturday p3 temperature", + "name": "saturday_p3_temperature", + "property": "saturday_p3_temperature", + "type": "numeric", + "unit": "°C", + "value_max": 35, + "value_min": 5, + "value_step": 0.5 + }, + { + "access": 3, + "label": "Saturday p4 hour", + "name": "saturday_p4_hour", + "property": "saturday_p4_hour", + "type": "numeric", + "unit": "h", + "value_max": 23, + "value_min": 0 + }, + { + "access": 3, + "label": "Saturday p4 minute", + "name": "saturday_p4_minute", + "property": "saturday_p4_minute", + "type": "numeric", + "unit": "m", + "value_max": 59, + "value_min": 0 + }, + { + "access": 3, + "label": "Saturday p4 temperature", + "name": "saturday_p4_temperature", + "property": "saturday_p4_temperature", + "type": "numeric", + "unit": "°C", + "value_max": 35, + "value_min": 5, + "value_step": 0.5 + }, + { + "access": 3, + "label": "Sunday p1 hour", + "name": "sunday_p1_hour", + "property": "sunday_p1_hour", + "type": "numeric", + "unit": "h", + "value_max": 23, + "value_min": 0 + }, + { + "access": 3, + "label": "Sunday p1 minute", + "name": "sunday_p1_minute", + "property": "sunday_p1_minute", + "type": "numeric", + "unit": "m", + "value_max": 59, + "value_min": 0 + }, + { + "access": 3, + "label": "Sunday p1 temperature", + "name": "sunday_p1_temperature", + "property": "sunday_p1_temperature", + "type": "numeric", + "unit": "°C", + "value_max": 35, + "value_min": 5, + "value_step": 0.5 + }, + { + "access": 3, + "label": "Sunday p2 hour", + "name": "sunday_p2_hour", + "property": "sunday_p2_hour", + "type": "numeric", + "unit": "h", + "value_max": 23, + "value_min": 0 + }, + { + "access": 3, + "label": "Sunday p2 minute", + "name": "sunday_p2_minute", + "property": "sunday_p2_minute", + "type": "numeric", + "unit": "m", + "value_max": 59, + "value_min": 0 + }, + { + "access": 3, + "label": "Sunday p2 temperature", + "name": "sunday_p2_temperature", + "property": "sunday_p2_temperature", + "type": "numeric", + "unit": "°C", + "value_max": 35, + "value_min": 5, + "value_step": 0.5 + }, + { + "access": 3, + "label": "Sunday p3 hour", + "name": "sunday_p3_hour", + "property": "sunday_p3_hour", + "type": "numeric", + "unit": "h", + "value_max": 23, + "value_min": 0 + }, + { + "access": 3, + "label": "Sunday p3 minute", + "name": "sunday_p3_minute", + "property": "sunday_p3_minute", + "type": "numeric", + "unit": "m", + "value_max": 59, + "value_min": 0 + }, + { + "access": 3, + "label": "Sunday p3 temperature", + "name": "sunday_p3_temperature", + "property": "sunday_p3_temperature", + "type": "numeric", + "unit": "°C", + "value_max": 35, + "value_min": 5, + "value_step": 0.5 + }, + { + "access": 3, + "label": "Sunday p4 hour", + "name": "sunday_p4_hour", + "property": "sunday_p4_hour", + "type": "numeric", + "unit": "h", + "value_max": 23, + "value_min": 0 + }, + { + "access": 3, + "label": "Sunday p4 minute", + "name": "sunday_p4_minute", + "property": "sunday_p4_minute", + "type": "numeric", + "unit": "m", + "value_max": 59, + "value_min": 0 + }, + { + "access": 3, + "label": "Sunday p4 temperature", + "name": "sunday_p4_temperature", + "property": "sunday_p4_temperature", + "type": "numeric", + "unit": "°C", + "value_max": 35, + "value_min": 5, + "value_step": 0.5 + } + ], + "label": "Program", + "name": "program", + "property": "program", + "type": "composite" + } + ], + "model": "BHT-002/BHT-006", + "options": [], + "supports_ota": false, + "vendor": "Moes" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genGroups", + "genScenes", + "manuSpecificTuya", + "genBasic" + ], + "output": [ + "genOta", + "genTime" + ] + }, + "configured_reportings": [], + "scenes": [] + }, + "242": { + "bindings": [], + "clusters": { + "input": [], + "output": [ + "greenPower" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Moes thermo", + "ieee_address": "0xa4c138f402dfba8a", + "interview_completed": true, + "interviewing": false, + "manufacturer": "_TZE204_aoclfnxz", + "model_id": "TS0601", + "network_address": 47713, + "power_source": "Mains (single phase)", + "supported": true, + "type": "Router" + }, + { + "date_code": "", + "definition": { + "description": "Smart light switch module (2 gang)", + "exposes": [ + { + "endpoint": "l1", + "features": [ + { + "access": 7, + "description": "On/off state of the switch", + "endpoint": "l1", + "label": "State", + "name": "state", + "property": "state_l1", + "type": "binary", + "value_off": "OFF", + "value_on": "ON", + "value_toggle": "TOGGLE" + } + ], + "type": "switch" + }, + { + "endpoint": "l2", + "features": [ + { + "access": 7, + "description": "On/off state of the switch", + "endpoint": "l2", + "label": "State", + "name": "state", + "property": "state_l2", + "type": "binary", + "value_off": "OFF", + "value_on": "ON", + "value_toggle": "TOGGLE" + } + ], + "type": "switch" + }, + { + "access": 7, + "category": "config", + "description": "Controls the behavior when the device is powered on after power loss", + "label": "Power-on behavior", + "name": "power_on_behavior", + "property": "power_on_behavior", + "type": "enum", + "values": [ + "off", + "previous", + "on" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "MS-104BZ", + "options": [ + { + "access": 2, + "description": "State actions will also be published as 'action' when true (default false).", + "label": "State action", + "name": "state_action", + "property": "state_action", + "type": "binary", + "value_off": false, + "value_on": true + } + ], + "supports_ota": false, + "vendor": "Moes" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [ + { + "cluster": "genOnOff", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + } + ], + "clusters": { + "input": [ + "genBasic", + "genIdentify", + "genGroups", + "genScenes", + "genOnOff", + "57344", + "manuSpecificTuya_3" + ], + "output": [ + "genOta", + "genTime" + ] + }, + "configured_reportings": [ + { + "attribute": "onOff", + "cluster": "genOnOff", + "maximum_report_interval": 3600, + "minimum_report_interval": 0, + "reportable_change": 0 + } + ], + "scenes": [] + }, + "2": { + "bindings": [ + { + "cluster": "genOnOff", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + } + ], + "clusters": { + "input": [ + "genIdentify", + "genGroups", + "genScenes", + "genOnOff", + "57344", + "manuSpecificTuya_3" + ], + "output": [] + }, + "configured_reportings": [ + { + "attribute": "onOff", + "cluster": "genOnOff", + "maximum_report_interval": 3600, + "minimum_report_interval": 0, + "reportable_change": 0 + } + ], + "scenes": [] + }, + "242": { + "bindings": [], + "clusters": { + "input": [], + "output": [ + "greenPower" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Moes switch double", + "ieee_address": "0xcc86ecfffe4e9d25", + "interview_completed": true, + "interviewing": false, + "manufacturer": "_TZ3000_pmz6mjyu", + "model_id": "TS011F", + "network_address": 49638, + "power_source": "Mains (single phase)", + "supported": true, + "type": "Router" + }, + { + "date_code": "20200310", + "definition": { + "description": "Water leak sensor", + "exposes": [ + { + "access": 1, + "category": "diagnostic", + "description": "Remaining battery in %, can take up to 24 hours before reported", + "label": "Battery", + "name": "battery", + "property": "battery", + "type": "numeric", + "unit": "%", + "value_max": 100, + "value_min": 0 + }, + { + "access": 1, + "description": "Indicates whether the device detected a water leak", + "label": "Water leak", + "name": "water_leak", + "property": "water_leak", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 1, + "category": "diagnostic", + "description": "Indicates if the battery of this device is almost empty", + "label": "Battery low", + "name": "battery_low", + "property": "battery_low", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 1, + "category": "diagnostic", + "description": "Voltage of the battery in millivolts", + "label": "Voltage", + "name": "voltage", + "property": "voltage", + "type": "numeric", + "unit": "mV" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Temperature of the device", + "label": "Device temperature", + "name": "device_temperature", + "property": "device_temperature", + "type": "numeric", + "unit": "°C" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Number of power outages", + "label": "Power outage count", + "name": "power_outage_count", + "property": "power_outage_count", + "type": "numeric" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Indicates how many times the sensor was triggered (since last scheduled report)", + "label": "Trigger count", + "name": "trigger_count", + "property": "trigger_count", + "type": "numeric" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "SJCGQ11LM", + "options": [ + { + "access": 2, + "description": "Calibrates the device_temperature value (absolute offset), takes into effect on next report of device.", + "label": "Device temperature calibration", + "name": "device_temperature_calibration", + "property": "device_temperature_calibration", + "type": "numeric" + } + ], + "supports_ota": false, + "vendor": "Aqara" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genBasic", + "genIdentify", + "genPowerCfg" + ], + "output": [ + "genOta" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Leak sensor", + "ieee_address": "0x00158d00094d5a84", + "interview_completed": true, + "interviewing": false, + "manufacturer": "LUMI", + "model_id": "lumi.sensor_wleak.aq1", + "network_address": 7848, + "power_source": "Battery", + "software_build_id": "3000-0001", + "supported": true, + "type": "EndDevice" + }, + { + "date_code": "20220906", + "definition": { + "description": "Zigbee GU10 LED lamp", + "exposes": [ + { + "features": [ + { + "access": 7, + "description": "On/off state of this light", + "label": "State", + "name": "state", + "property": "state", + "type": "binary", + "value_off": "OFF", + "value_on": "ON", + "value_toggle": "TOGGLE" + }, + { + "access": 7, + "description": "Brightness of this light", + "label": "Brightness", + "name": "brightness", + "property": "brightness", + "type": "numeric", + "value_max": 254, + "value_min": 0 + }, + { + "access": 7, + "description": "Color temperature of this light", + "label": "Color temp", + "name": "color_temp", + "presets": [ + { + "description": "Coolest temperature supported", + "name": "coolest", + "value": 158 + }, + { + "description": "Cool temperature (250 mireds / 4000 Kelvin)", + "name": "cool", + "value": 250 + }, + { + "description": "Neutral temperature (370 mireds / 2700 Kelvin)", + "name": "neutral", + "value": 370 + }, + { + "description": "Warm temperature (454 mireds / 2200 Kelvin)", + "name": "warm", + "value": 454 + }, + { + "description": "Warmest temperature supported", + "name": "warmest", + "value": 495 + } + ], + "property": "color_temp", + "type": "numeric", + "unit": "mired", + "value_max": 495, + "value_min": 158 + }, + { + "access": 7, + "description": "Color temperature after cold power on of this light", + "label": "Color temp startup", + "name": "color_temp_startup", + "presets": [ + { + "description": "Coolest temperature supported", + "name": "coolest", + "value": 158 + }, + { + "description": "Cool temperature (250 mireds / 4000 Kelvin)", + "name": "cool", + "value": 250 + }, + { + "description": "Neutral temperature (370 mireds / 2700 Kelvin)", + "name": "neutral", + "value": 370 + }, + { + "description": "Warm temperature (454 mireds / 2200 Kelvin)", + "name": "warm", + "value": 454 + }, + { + "description": "Warmest temperature supported", + "name": "warmest", + "value": 495 + }, + { + "description": "Restore previous color_temp on cold power on", + "name": "previous", + "value": 65535 + } + ], + "property": "color_temp_startup", + "type": "numeric", + "unit": "mired", + "value_max": 495, + "value_min": 158 + }, + { + "access": 7, + "description": "Color of this light in the CIE 1931 color space (x/y)", + "features": [ + { + "access": 7, + "label": "X", + "name": "x", + "property": "x", + "type": "numeric" + }, + { + "access": 7, + "label": "Y", + "name": "y", + "property": "y", + "type": "numeric" + } + ], + "label": "Color (X/Y)", + "name": "color_xy", + "property": "color", + "type": "composite" + }, + { + "access": 7, + "description": "Color of this light expressed as hue/saturation", + "features": [ + { + "access": 7, + "label": "Hue", + "name": "hue", + "property": "hue", + "type": "numeric" + }, + { + "access": 7, + "label": "Saturation", + "name": "saturation", + "property": "saturation", + "type": "numeric" + } + ], + "label": "Color (HS)", + "name": "color_hs", + "property": "color", + "type": "composite" + } + ], + "type": "light" + }, + { + "access": 2, + "description": "Triggers an effect on the light (e.g. make light blink for a few seconds)", + "label": "Effect", + "name": "effect", + "property": "effect", + "type": "enum", + "values": [ + "blink", + "breathe", + "okay", + "channel_change", + "finish_effect", + "stop_effect", + "colorloop", + "stop_colorloop" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "GL-S-006P", + "options": [ + { + "access": 2, + "description": "Controls the transition time (in seconds) of on/off, brightness, color temperature (if applicable) and color (if applicable) changes. Defaults to `0` (no transition).", + "label": "Transition", + "name": "transition", + "property": "transition", + "type": "numeric", + "value_min": 0 + }, + { + "access": 2, + "description": "When enabled colors will be synced, e.g. if the light supports both color x/y and color temperature a conversion from color x/y to color temperature will be done when setting the x/y color (default true).", + "label": "Color sync", + "name": "color_sync", + "property": "color_sync", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 2, + "description": "State actions will also be published as 'action' when true (default false).", + "label": "State action", + "name": "state_action", + "property": "state_action", + "type": "binary", + "value_off": false, + "value_on": true + } + ], + "supports_ota": true, + "vendor": "Gledopto" + }, + "disabled": false, + "endpoints": { + "11": { + "bindings": [ + { + "cluster": "genOnOff", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + } + ], + "clusters": { + "input": [ + "genBasic", + "genIdentify", + "genGroups", + "genScenes", + "genOnOff", + "genLevelCtrl", + "lightingColorCtrl", + "touchlink" + ], + "output": [ + "genOta" + ] + }, + "configured_reportings": [ + { + "attribute": "onOff", + "cluster": "genOnOff", + "maximum_report_interval": 3600, + "minimum_report_interval": 60, + "reportable_change": 0 + } + ], + "scenes": [] + }, + "242": { + "bindings": [], + "clusters": { + "input": [], + "output": [ + "greenPower" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Gledopto RGBCTT light I", + "ieee_address": "0xa4c138071003495f", + "interview_completed": true, + "interviewing": false, + "manufacturer": "GLEDOPTO", + "model_id": "GL-S-006P", + "network_address": 48110, + "power_source": "Mains (single phase)", + "software_build_id": "10751203", + "supported": true, + "type": "Router" + }, + { + "date_code": "", + "definition": { + "description": "Smart button", + "exposes": [ + { + "access": 1, + "category": "diagnostic", + "description": "Triggered action (e.g. a button click)", + "label": "Action", + "name": "action", + "property": "action", + "type": "enum", + "values": [ + "single", + "double", + "hold", + "brightness_move_to_level", + "color_temperature_move", + "brightness_step_up", + "brightness_step_down", + "on", + "off" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Remaining battery in %, can take up to 24 hours before reported", + "label": "Battery", + "name": "battery", + "property": "battery", + "type": "numeric", + "unit": "%", + "value_max": 100, + "value_min": 0 + }, + { + "access": 7, + "description": "Operation mode: \"command\" - for group control, \"event\" - for clicks", + "label": "Operation mode", + "name": "operation_mode", + "property": "operation_mode", + "type": "enum", + "values": [ + "command", + "event" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "ERS-10TZBVB-AA", + "options": [ + { + "access": 2, + "description": "Simulate a brightness value. If this device provides a brightness_move_up or brightness_move_down action it is possible to specify the update interval and delta. The action_brightness_delta indicates the delta for each interval. ", + "features": [ + { + "access": 2, + "description": "Delta per interval, 20 by default", + "label": "Delta", + "name": "delta", + "property": "delta", + "type": "numeric", + "value_min": 0 + }, + { + "access": 2, + "description": "Interval duration", + "label": "Interval", + "name": "interval", + "property": "interval", + "type": "numeric", + "unit": "ms", + "value_min": 0 + } + ], + "label": "Simulated brightness", + "name": "simulated_brightness", + "property": "simulated_brightness", + "type": "composite" + } + ], + "supports_ota": false, + "vendor": "Moes" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [ + { + "cluster": "genPowerCfg", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + }, + { + "cluster": "genOnOff", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + } + ], + "clusters": { + "input": [ + "genBasic", + "genPowerCfg", + "genIdentify", + "genGroups", + "genOnOff", + "touchlink", + "manuSpecificTuya_3" + ], + "output": [ + "genOta", + "genTime", + "genIdentify", + "genGroups", + "genOnOff", + "genLevelCtrl", + "touchlink" + ] + }, + "configured_reportings": [ + { + "attribute": "batteryPercentageRemaining", + "cluster": "genPowerCfg", + "maximum_report_interval": 65000, + "minimum_report_interval": 3600, + "reportable_change": 0 + } + ], + "scenes": [] + } + }, + "friendly_name": "Smart button", + "ieee_address": "0x385b44fffe20cfb2", + "interview_completed": true, + "interviewing": false, + "manufacturer": "_TZ3000_kjfzuycl", + "model_id": "TS004F", + "network_address": 57475, + "power_source": "Battery", + "supported": true, + "type": "EndDevice" + }, + { + "date_code": "20221115 03:44", + "definition": { + "description": "Air quality sensor", + "exposes": [ + { + "access": 5, + "description": "Measured VOC value", + "label": "Voc", + "name": "voc", + "property": "voc", + "type": "numeric", + "unit": "µg/m³" + }, + { + "access": 1, + "description": "Measured air quality", + "label": "Air quality", + "name": "air_quality", + "property": "air_quality", + "type": "enum", + "values": [ + "excellent", + "good", + "moderate", + "poor", + "unhealthy", + "out_of_range", + "unknown" + ] + }, + { + "access": 5, + "description": "Measured temperature value", + "label": "Temperature", + "name": "temperature", + "property": "temperature", + "type": "numeric", + "unit": "°C" + }, + { + "access": 5, + "description": "Measured relative humidity", + "label": "Humidity", + "name": "humidity", + "property": "humidity", + "type": "numeric", + "unit": "%" + }, + { + "access": 5, + "category": "diagnostic", + "description": "Remaining battery in %", + "label": "Battery", + "name": "battery", + "property": "battery", + "type": "numeric", + "unit": "%", + "value_max": 100, + "value_min": 0 + }, + { + "access": 5, + "category": "diagnostic", + "description": "Reported battery voltage in millivolts", + "label": "Voltage", + "name": "voltage", + "property": "voltage", + "type": "numeric", + "unit": "mV" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Indicates if the battery of this device is almost empty", + "label": "Battery low", + "name": "battery_low", + "property": "battery_low", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "AQSZB-110", + "options": [ + { + "access": 2, + "description": "Calibrates the voc value (absolute offset), takes into effect on next report of device.", + "label": "Voc calibration", + "name": "voc_calibration", + "property": "voc_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Calibrates the temperature value (absolute offset), takes into effect on next report of device.", + "label": "Temperature calibration", + "name": "temperature_calibration", + "property": "temperature_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for temperature, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Temperature precision", + "name": "temperature_precision", + "property": "temperature_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the humidity value (absolute offset), takes into effect on next report of device.", + "label": "Humidity calibration", + "name": "humidity_calibration", + "property": "humidity_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for humidity, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Humidity precision", + "name": "humidity_precision", + "property": "humidity_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + } + ], + "supports_ota": true, + "vendor": "Develco" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genIdentify", + "genScenes", + "genOnOff" + ], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + }, + "38": { + "bindings": [ + { + "cluster": "genPollCtrl", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + }, + { + "cluster": "manuSpecificDevelcoAirQuality", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + }, + { + "cluster": "msTemperatureMeasurement", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + }, + { + "cluster": "msRelativeHumidity", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + }, + { + "cluster": "genPowerCfg", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + } + ], + "clusters": { + "input": [ + "genBasic", + "genPowerCfg", + "genIdentify", + "genPollCtrl", + "msTemperatureMeasurement", + "msRelativeHumidity", + "1070", + "manuSpecificDevelcoAirQuality" + ], + "output": [ + "genIdentify", + "genTime", + "genOta" + ] + }, + "configured_reportings": [ + { + "attribute": "measuredValue", + "cluster": "manuSpecificDevelcoAirQuality", + "maximum_report_interval": 3600, + "minimum_report_interval": 60, + "reportable_change": 10 + }, + { + "attribute": "measuredValue", + "cluster": "msTemperatureMeasurement", + "maximum_report_interval": 600, + "minimum_report_interval": 60, + "reportable_change": 10 + }, + { + "attribute": "measuredValue", + "cluster": "msRelativeHumidity", + "maximum_report_interval": 600, + "minimum_report_interval": 60, + "reportable_change": 300 + }, + { + "attribute": "batteryVoltage", + "cluster": "genPowerCfg", + "maximum_report_interval": 43200, + "minimum_report_interval": 3600, + "reportable_change": 100 + } + ], + "scenes": [] + } + }, + "friendly_name": "Develco air quality", + "ieee_address": "0x0015bc0036001b9e", + "interview_completed": true, + "interviewing": false, + "manufacturer": "frient A/S", + "model_id": "AQSZB-110", + "network_address": 20064, + "power_source": "Battery", + "software_build_id": "4.0.1", + "supported": true, + "type": "EndDevice" + }, + { + "date_code": "12-09-2020", + "definition": { + "description": "Single switch module T1 (no neutral)", + "exposes": [ + { + "features": [ + { + "access": 7, + "description": "On/off state of the switch", + "label": "State", + "name": "state", + "property": "state", + "type": "binary", + "value_off": "OFF", + "value_on": "ON", + "value_toggle": "TOGGLE" + } + ], + "type": "switch" + }, + { + "access": 7, + "category": "config", + "description": "Enable/disable the power outage memory, this recovers the on/off mode after power failure", + "label": "Power outage memory", + "name": "power_outage_memory", + "property": "power_outage_memory", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 7, + "description": "Wall switch type", + "label": "Switch type", + "name": "switch_type", + "property": "switch_type", + "type": "enum", + "values": [ + "toggle", + "momentary" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Number of power outages (since last pairing)", + "label": "Power outage count", + "name": "power_outage_count", + "property": "power_outage_count", + "type": "numeric" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Temperature of the device", + "label": "Device temperature", + "name": "device_temperature", + "property": "device_temperature", + "type": "numeric", + "unit": "°C" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "SSM-U02", + "options": [ + { + "access": 2, + "description": "Calibrates the device_temperature value (absolute offset), takes into effect on next report of device.", + "label": "Device temperature calibration", + "name": "device_temperature_calibration", + "property": "device_temperature_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "State actions will also be published as 'action' when true (default false).", + "label": "State action", + "name": "state_action", + "property": "state_action", + "type": "binary", + "value_off": false, + "value_on": true + } + ], + "supports_ota": false, + "vendor": "Aqara" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [ + { + "cluster": "genOnOff", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + } + ], + "clusters": { + "input": [ + "genBasic", + "genDeviceTempCfg", + "genIdentify", + "genGroups", + "genScenes", + "genOnOff", + "genMultistateInput", + "manuSpecificLumi" + ], + "output": [ + "genTime", + "genOta" + ] + }, + "configured_reportings": [ + { + "attribute": "onOff", + "cluster": "genOnOff", + "maximum_report_interval": 3600, + "minimum_report_interval": 0, + "reportable_change": 0 + } + ], + "scenes": [] + }, + "41": { + "bindings": [], + "clusters": { + "input": [ + "genMultistateInput" + ], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + }, + "242": { + "bindings": [], + "clusters": { + "input": [], + "output": [ + "greenPower" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Aqara switch no neutral", + "ieee_address": "0x54ef4410004695ae", + "interview_completed": true, + "interviewing": false, + "manufacturer": "LUMI", + "model_id": "lumi.switch.l0agl1", + "network_address": 53592, + "power_source": "Mains (single phase)", + "supported": true, + "type": "EndDevice" + }, + { + "definition": { + "description": "Luminance motion sensor", + "exposes": [ + { + "access": 1, + "description": "Indicates whether the device detected occupancy", + "label": "Occupancy", + "name": "occupancy", + "property": "occupancy", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 1, + "description": "Raw measured illuminance", + "label": "Illuminance", + "name": "illuminance", + "property": "illuminance", + "type": "numeric", + "unit": "lx" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Remaining battery in %, can take up to 24 hours before reported", + "label": "Battery", + "name": "battery", + "property": "battery", + "type": "numeric", + "unit": "%", + "value_max": 100, + "value_min": 0 + }, + { + "access": 3, + "description": "PIR sensor sensitivity (refresh and update only while active)", + "label": "Sensitivity", + "name": "sensitivity", + "property": "sensitivity", + "type": "enum", + "values": [ + "low", + "medium", + "high" + ] + }, + { + "access": 3, + "description": "PIR keep time in seconds (refresh and update only while active)", + "label": "Keep time", + "name": "keep_time", + "property": "keep_time", + "type": "enum", + "values": [ + "10", + "30", + "60", + "120" + ] + }, + { + "access": 3, + "description": "Brightness acquisition interval (refresh and update only while active)", + "label": "Illuminance interval", + "name": "illuminance_interval", + "property": "illuminance_interval", + "type": "numeric", + "unit": "minutes", + "value_max": 720, + "value_min": 1, + "value_step": 1 + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "ZG-204ZL", + "options": [ + { + "access": 2, + "description": "Calibrates the illuminance value (percentual offset), takes into effect on next report of device.", + "label": "Illuminance calibration", + "name": "illuminance_calibration", + "property": "illuminance_calibration", + "type": "numeric" + } + ], + "supports_ota": false, + "vendor": "Tuya" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genBasic", + "genIdentify", + "ssIasZone", + "manuSpecificTuya_2", + "manuSpecificTuya", + "60928", + "57344", + "genPowerCfg", + "msIlluminanceMeasurement" + ], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Mini luminance motion sensor", + "ieee_address": "0xa4c138dbda3532cb", + "interview_completed": true, + "interviewing": false, + "manufacturer": "_TZE200_3towulqd", + "model_id": "TS0601", + "network_address": 19333, + "power_source": "Battery", + "software_build_id": "0122052017", + "supported": true, + "type": "EndDevice" + }, + { + "date_code": "", + "definition": { + "description": "RGB+CCT 4.7W GU10 LED bulb", + "exposes": [ + { + "features": [ + { + "access": 7, + "description": "On/off state of this light", + "label": "State", + "name": "state", + "property": "state", + "type": "binary", + "value_off": "OFF", + "value_on": "ON", + "value_toggle": "TOGGLE" + }, + { + "access": 7, + "description": "Brightness of this light", + "label": "Brightness", + "name": "brightness", + "property": "brightness", + "type": "numeric", + "value_max": 254, + "value_min": 0 + }, + { + "access": 7, + "description": "Color temperature of this light", + "label": "Color temp", + "name": "color_temp", + "presets": [ + { + "description": "Coolest temperature supported", + "name": "coolest", + "value": 153 + }, + { + "description": "Cool temperature (250 mireds / 4000 Kelvin)", + "name": "cool", + "value": 250 + }, + { + "description": "Neutral temperature (370 mireds / 2700 Kelvin)", + "name": "neutral", + "value": 370 + }, + { + "description": "Warm temperature (454 mireds / 2200 Kelvin)", + "name": "warm", + "value": 454 + }, + { + "description": "Warmest temperature supported", + "name": "warmest", + "value": 500 + } + ], + "property": "color_temp", + "type": "numeric", + "unit": "mired", + "value_max": 500, + "value_min": 153 + }, + { + "access": 7, + "description": "Color of this light in the CIE 1931 color space (x/y)", + "features": [ + { + "access": 7, + "label": "X", + "name": "x", + "property": "x", + "type": "numeric" + }, + { + "access": 7, + "label": "Y", + "name": "y", + "property": "y", + "type": "numeric" + } + ], + "label": "Color (X/Y)", + "name": "color_xy", + "property": "color", + "type": "composite" + } + ], + "type": "light" + }, + { + "access": 2, + "description": "Triggers an effect on the light (e.g. make light blink for a few seconds)", + "label": "Effect", + "name": "effect", + "property": "effect", + "type": "enum", + "values": [ + "blink", + "breathe", + "okay", + "channel_change", + "finish_effect", + "stop_effect", + "colorloop", + "stop_colorloop" + ] + }, + { + "access": 3, + "description": "Do not disturb mode, when enabled this function will keep the light OFF after a power outage", + "label": "Do not disturb", + "name": "do_not_disturb", + "property": "do_not_disturb", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 3, + "description": "Power on behavior state", + "label": "Color power on behavior", + "name": "color_power_on_behavior", + "property": "color_power_on_behavior", + "type": "enum", + "values": [ + "initial", + "previous", + "customized" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "ZB-TD5-RCW-GU10", + "options": [ + { + "access": 2, + "description": "Controls the transition time (in seconds) of on/off, brightness, color temperature (if applicable) and color (if applicable) changes. Defaults to `0` (no transition).", + "label": "Transition", + "name": "transition", + "property": "transition", + "type": "numeric", + "value_min": 0 + }, + { + "access": 2, + "description": "When enabled colors will be synced, e.g. if the light supports both color x/y and color temperature a conversion from color x/y to color temperature will be done when setting the x/y color (default true).", + "label": "Color sync", + "name": "color_sync", + "property": "color_sync", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 2, + "description": "State actions will also be published as 'action' when true (default false).", + "label": "State action", + "name": "state_action", + "property": "state_action", + "type": "binary", + "value_off": false, + "value_on": true + } + ], + "supports_ota": false, + "vendor": "Moes" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genIdentify", + "genGroups", + "genScenes", + "genOnOff", + "touchlink", + "genLevelCtrl", + "lightingColorCtrl", + "manuSpecificTuya", + "genBasic" + ], + "output": [ + "genOta", + "genTime" + ] + }, + "configured_reportings": [], + "scenes": [] + }, + "242": { + "bindings": [], + "clusters": { + "input": [], + "output": [ + "greenPower" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Moes RGBCCT light bulb", + "ieee_address": "0xa4c1388ad0ebb0a6", + "interview_completed": true, + "interviewing": false, + "manufacturer": "_TZ3210_rcggc0ys", + "model_id": "TS0505B", + "network_address": 44005, + "power_source": "Mains (single phase)", + "supported": true, + "type": "Router" + }, + { + "date_code": "20221018", + "definition": { + "description": "GU10 spot 350 lm, dimmable, RGBW", + "exposes": [ + { + "features": [ + { + "access": 7, + "description": "On/off state of this light", + "label": "State", + "name": "state", + "property": "state", + "type": "binary", + "value_off": "OFF", + "value_on": "ON", + "value_toggle": "TOGGLE" + }, + { + "access": 7, + "description": "Brightness of this light", + "label": "Brightness", + "name": "brightness", + "property": "brightness", + "type": "numeric", + "value_max": 254, + "value_min": 0 + }, + { + "access": 7, + "description": "Color temperature of this light", + "label": "Color temp", + "name": "color_temp", + "presets": [ + { + "description": "Coolest temperature supported", + "name": "coolest", + "value": 153 + }, + { + "description": "Cool temperature (250 mireds / 4000 Kelvin)", + "name": "cool", + "value": 250 + }, + { + "description": "Neutral temperature (370 mireds / 2700 Kelvin)", + "name": "neutral", + "value": 370 + }, + { + "description": "Warm temperature (454 mireds / 2200 Kelvin)", + "name": "warm", + "value": 454 + }, + { + "description": "Warmest temperature supported", + "name": "warmest", + "value": 555 + } + ], + "property": "color_temp", + "type": "numeric", + "unit": "mired", + "value_max": 555, + "value_min": 153 + }, + { + "access": 7, + "description": "Color temperature after cold power on of this light", + "label": "Color temp startup", + "name": "color_temp_startup", + "presets": [ + { + "description": "Coolest temperature supported", + "name": "coolest", + "value": 153 + }, + { + "description": "Cool temperature (250 mireds / 4000 Kelvin)", + "name": "cool", + "value": 250 + }, + { + "description": "Neutral temperature (370 mireds / 2700 Kelvin)", + "name": "neutral", + "value": 370 + }, + { + "description": "Warm temperature (454 mireds / 2200 Kelvin)", + "name": "warm", + "value": 454 + }, + { + "description": "Warmest temperature supported", + "name": "warmest", + "value": 555 + }, + { + "description": "Restore previous color_temp on cold power on", + "name": "previous", + "value": 65535 + } + ], + "property": "color_temp_startup", + "type": "numeric", + "unit": "mired", + "value_max": 555, + "value_min": 153 + }, + { + "access": 7, + "description": "Color of this light in the CIE 1931 color space (x/y)", + "features": [ + { + "access": 7, + "label": "X", + "name": "x", + "property": "x", + "type": "numeric" + }, + { + "access": 7, + "label": "Y", + "name": "y", + "property": "y", + "type": "numeric" + } + ], + "label": "Color (X/Y)", + "name": "color_xy", + "property": "color", + "type": "composite" + }, + { + "access": 7, + "description": "Color of this light expressed as hue/saturation", + "features": [ + { + "access": 7, + "label": "Hue", + "name": "hue", + "property": "hue", + "type": "numeric" + }, + { + "access": 7, + "label": "Saturation", + "name": "saturation", + "property": "saturation", + "type": "numeric" + } + ], + "label": "Color (HS)", + "name": "color_hs", + "property": "color", + "type": "composite" + } + ], + "type": "light" + }, + { + "access": 2, + "description": "Triggers an effect on the light (e.g. make light blink for a few seconds)", + "label": "Effect", + "name": "effect", + "property": "effect", + "type": "enum", + "values": [ + "blink", + "breathe", + "okay", + "channel_change", + "finish_effect", + "stop_effect", + "colorloop", + "stop_colorloop" + ] + }, + { + "access": 7, + "category": "config", + "description": "Controls the behavior when the device is powered on after power loss", + "label": "Power-on behavior", + "name": "power_on_behavior", + "property": "power_on_behavior", + "type": "enum", + "values": [ + "off", + "on", + "toggle", + "previous" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "RS 230 C", + "options": [ + { + "access": 2, + "description": "Controls the transition time (in seconds) of on/off, brightness, color temperature (if applicable) and color (if applicable) changes. Defaults to `0` (no transition).", + "label": "Transition", + "name": "transition", + "property": "transition", + "type": "numeric", + "value_min": 0 + }, + { + "access": 2, + "description": "When enabled colors will be synced, e.g. if the light supports both color x/y and color temperature a conversion from color x/y to color temperature will be done when setting the x/y color (default true).", + "label": "Color sync", + "name": "color_sync", + "property": "color_sync", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 2, + "description": "State actions will also be published as 'action' when true (default false).", + "label": "State action", + "name": "state_action", + "property": "state_action", + "type": "binary", + "value_off": false, + "value_on": true + } + ], + "supports_ota": true, + "vendor": "Innr" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genBasic", + "genIdentify", + "genGroups", + "genScenes", + "genOnOff", + "genLevelCtrl", + "lightingColorCtrl" + ], + "output": [ + "genOta" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Inrr RGBCTT light", + "ieee_address": "0x04cd15fffe0d0ebd", + "interview_completed": true, + "interviewing": false, + "manufacturer": "innr", + "model_id": "RS 230 C", + "network_address": 9493, + "power_source": "Mains (single phase)", + "software_build_id": "2.00.04", + "supported": true, + "type": "Router" + }, + { + "definition": { + "description": "LED RGB & brightness", + "exposes": [ + { + "features": [ + { + "access": 7, + "description": "On/off state of this light", + "label": "State", + "name": "state", + "property": "state", + "type": "binary", + "value_off": "OFF", + "value_on": "ON", + "value_toggle": "TOGGLE" + }, + { + "access": 7, + "description": "Brightness of this light", + "label": "Brightness", + "name": "brightness", + "property": "brightness", + "type": "numeric", + "value_max": 254, + "value_min": 0 + }, + { + "access": 7, + "description": "Color temperature of this light", + "label": "Color temp", + "name": "color_temp", + "presets": [ + { + "description": "Coolest temperature supported", + "name": "coolest", + "value": 153 + }, + { + "description": "Cool temperature (250 mireds / 4000 Kelvin)", + "name": "cool", + "value": 250 + }, + { + "description": "Neutral temperature (370 mireds / 2700 Kelvin)", + "name": "neutral", + "value": 370 + }, + { + "description": "Warmest temperature supported", + "name": "warmest", + "value": 370 + } + ], + "property": "color_temp", + "type": "numeric", + "unit": "mired", + "value_max": 370, + "value_min": 153 + }, + { + "access": 7, + "description": "Color temperature after cold power on of this light", + "label": "Color temp startup", + "name": "color_temp_startup", + "presets": [ + { + "description": "Coolest temperature supported", + "name": "coolest", + "value": 153 + }, + { + "description": "Cool temperature (250 mireds / 4000 Kelvin)", + "name": "cool", + "value": 250 + }, + { + "description": "Neutral temperature (370 mireds / 2700 Kelvin)", + "name": "neutral", + "value": 370 + }, + { + "description": "Warmest temperature supported", + "name": "warmest", + "value": 370 + }, + { + "description": "Restore previous color_temp on cold power on", + "name": "previous", + "value": 65535 + } + ], + "property": "color_temp_startup", + "type": "numeric", + "unit": "mired", + "value_max": 370, + "value_min": 153 + }, + { + "access": 7, + "description": "Color of this light in the CIE 1931 color space (x/y)", + "features": [ + { + "access": 7, + "label": "X", + "name": "x", + "property": "x", + "type": "numeric" + }, + { + "access": 7, + "label": "Y", + "name": "y", + "property": "y", + "type": "numeric" + } + ], + "label": "Color (X/Y)", + "name": "color_xy", + "property": "color", + "type": "composite" + }, + { + "access": 7, + "description": "Color of this light expressed as hue/saturation", + "features": [ + { + "access": 7, + "label": "Hue", + "name": "hue", + "property": "hue", + "type": "numeric" + }, + { + "access": 7, + "label": "Saturation", + "name": "saturation", + "property": "saturation", + "type": "numeric" + } + ], + "label": "Color (HS)", + "name": "color_hs", + "property": "color", + "type": "composite" + } + ], + "type": "light" + }, + { + "access": 2, + "description": "Triggers an effect on the light (e.g. make light blink for a few seconds)", + "label": "Effect", + "name": "effect", + "property": "effect", + "type": "enum", + "values": [ + "blink", + "breathe", + "okay", + "channel_change", + "finish_effect", + "stop_effect", + "colorloop", + "stop_colorloop" + ] + }, + { + "access": 7, + "category": "config", + "description": "Controls the behavior when the device is powered on after power loss", + "label": "Power-on behavior", + "name": "power_on_behavior", + "property": "power_on_behavior", + "type": "enum", + "values": [ + "off", + "on", + "toggle", + "previous" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "33943/33944/33946", + "options": [ + { + "access": 2, + "description": "Controls the transition time (in seconds) of on/off, brightness, color temperature (if applicable) and color (if applicable) changes. Defaults to `0` (no transition).", + "label": "Transition", + "name": "transition", + "property": "transition", + "type": "numeric", + "value_min": 0 + }, + { + "access": 2, + "description": "When enabled colors will be synced, e.g. if the light supports both color x/y and color temperature a conversion from color x/y to color temperature will be done when setting the x/y color (default true).", + "label": "Color sync", + "name": "color_sync", + "property": "color_sync", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 2, + "description": "State actions will also be published as 'action' when true (default false).", + "label": "State action", + "name": "state_action", + "property": "state_action", + "type": "binary", + "value_off": false, + "value_on": true + } + ], + "supports_ota": false, + "vendor": "AwoX" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genBasic", + "genIdentify", + "genGroups", + "genScenes", + "genOnOff", + "genLevelCtrl", + "lightingColorCtrl", + "touchlink", + "manuSpecificAmazonWWAH" + ], + "output": [ + "genOnOff" + ] + }, + "configured_reportings": [], + "scenes": [] + }, + "3": { + "bindings": [], + "clusters": { + "input": [ + "65360", + "65361" + ], + "output": [ + "65360", + "65361" + ] + }, + "configured_reportings": [], + "scenes": [] + }, + "242": { + "bindings": [], + "clusters": { + "input": [], + "output": [ + "greenPower" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Eglo RGBCTT light", + "ieee_address": "0xa4c138363dff5c1d", + "interview_completed": true, + "interviewing": false, + "manufacturer": "AwoX", + "model_id": "TLSR82xx", + "network_address": 38981, + "power_source": "Mains (single phase)", + "software_build_id": "0122052017", + "supported": true, + "type": "Router" + }, + { + "date_code": "", + "definition": { + "description": "Carbon monoxide alarm", + "exposes": [ + { + "access": 1, + "description": "Indicates if CO (carbon monoxide) is detected", + "label": "Carbon monoxide", + "name": "carbon_monoxide", + "property": "carbon_monoxide", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 1, + "description": "The measured CO (carbon monoxide) value", + "label": "CO", + "name": "co", + "property": "co", + "type": "numeric", + "unit": "ppm" + }, + { + "access": 1, + "description": "Result of the self-test", + "label": "Self test result", + "name": "self_test_result", + "property": "self_test_result", + "type": "enum", + "values": [ + "checking", + "success", + "failure", + "others" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Remaining battery in %, can take up to 24 hours before reported", + "label": "Battery", + "name": "battery", + "property": "battery", + "type": "numeric", + "unit": "%", + "value_max": 100, + "value_min": 0 + }, + { + "access": 3, + "description": "Silence the alarm", + "label": "Silence", + "name": "silence", + "property": "silence", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "ZC-HM", + "options": [ + { + "access": 2, + "description": "Calibrates the co value (absolute offset), takes into effect on next report of device.", + "label": "Co calibration", + "name": "co_calibration", + "property": "co_calibration", + "type": "numeric" + } + ], + "supports_ota": false, + "vendor": "Moes" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genGroups", + "genScenes", + "manuSpecificTuya", + "genBasic" + ], + "output": [ + "genOta", + "genTime" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Carbon Monoxide sensor", + "ieee_address": "0xa4c138636c5c666e", + "interview_completed": true, + "interviewing": false, + "manufacturer": "_TZE200_rjxqso4a", + "model_id": "TS0601", + "network_address": 9251, + "power_source": "Battery", + "supported": true, + "type": "EndDevice" + }, + { + "definition": { + "description": "Vibration sensor", + "exposes": [ + { + "access": 1, + "category": "diagnostic", + "description": "Remaining battery in %, can take up to 24 hours before reported", + "label": "Battery", + "name": "battery", + "property": "battery", + "type": "numeric", + "unit": "%", + "value_max": 100, + "value_min": 0 + }, + { + "access": 1, + "category": "diagnostic", + "description": "Temperature of the device", + "label": "Device temperature", + "name": "device_temperature", + "property": "device_temperature", + "type": "numeric", + "unit": "°C" + }, + { + "access": 1, + "description": "Indicates whether the device detected vibration", + "label": "Vibration", + "name": "vibration", + "property": "vibration", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 1, + "label": "Strength", + "name": "strength", + "property": "strength", + "type": "numeric" + }, + { + "access": 3, + "label": "Sensitivity", + "name": "sensitivity", + "property": "sensitivity", + "type": "enum", + "values": [ + "low", + "medium", + "high" + ] + }, + { + "access": 1, + "label": "Angle x", + "name": "angle_x", + "property": "angle_x", + "type": "numeric", + "unit": "°", + "value_max": 90, + "value_min": -90 + }, + { + "access": 1, + "label": "Angle y", + "name": "angle_y", + "property": "angle_y", + "type": "numeric", + "unit": "°", + "value_max": 90, + "value_min": -90 + }, + { + "access": 1, + "label": "Angle z", + "name": "angle_z", + "property": "angle_z", + "type": "numeric", + "unit": "°", + "value_max": 90, + "value_min": -90 + }, + { + "access": 1, + "description": "Accelerometer X value", + "label": "X axis", + "name": "x_axis", + "property": "x_axis", + "type": "numeric" + }, + { + "access": 1, + "description": "Accelerometer Y value", + "label": "Y axis", + "name": "y_axis", + "property": "y_axis", + "type": "numeric" + }, + { + "access": 1, + "description": "Accelerometer Z value", + "label": "Z axis", + "name": "z_axis", + "property": "z_axis", + "type": "numeric" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Voltage of the battery in millivolts", + "label": "Voltage", + "name": "voltage", + "property": "voltage", + "type": "numeric", + "unit": "mV" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Number of power outages", + "label": "Power outage count", + "name": "power_outage_count", + "property": "power_outage_count", + "type": "numeric" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Triggered action (e.g. a button click)", + "label": "Action", + "name": "action", + "property": "action", + "type": "enum", + "values": [ + "vibration", + "tilt", + "drop" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "DJT11LM", + "options": [ + { + "access": 2, + "description": "Calibrates the device_temperature value (absolute offset), takes into effect on next report of device.", + "label": "Device temperature calibration", + "name": "device_temperature_calibration", + "property": "device_temperature_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Time in seconds after which vibration is cleared after detecting it (default 90 seconds).", + "label": "Vibration timeout", + "name": "vibration_timeout", + "property": "vibration_timeout", + "type": "numeric", + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the x value (absolute offset), takes into effect on next report of device.", + "label": "X calibration", + "name": "x_calibration", + "property": "x_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Calibrates the y value (absolute offset), takes into effect on next report of device.", + "label": "Y calibration", + "name": "y_calibration", + "property": "y_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Calibrates the z value (absolute offset), takes into effect on next report of device.", + "label": "Z calibration", + "name": "z_calibration", + "property": "z_calibration", + "type": "numeric" + } + ], + "supports_ota": false, + "vendor": "Aqara" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + }, + "2": { + "bindings": [], + "clusters": { + "input": [], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Vibration sensor", + "ieee_address": "0x00158d0009e19292", + "interview_completed": true, + "interviewing": false, + "manufacturer": "LUMI", + "model_id": "lumi.vibration.aq1", + "network_address": 65027, + "power_source": "Battery", + "supported": true, + "type": "EndDevice" + }, + { + "definition": { + "description": "5.8Ghz/24Ghz Human presence sensor", + "exposes": [ + { + "access": 1, + "description": "Indicates whether the device detected presence", + "label": "Presence", + "name": "presence", + "property": "presence", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 1, + "description": "Raw measured illuminance", + "label": "Illuminance", + "name": "illuminance", + "property": "illuminance", + "type": "numeric", + "unit": "lx" + }, + { + "access": 3, + "description": "Motion detection sensitivity", + "label": "Large motion detection sensitivity", + "name": "large_motion_detection_sensitivity", + "property": "large_motion_detection_sensitivity", + "type": "numeric", + "unit": "x", + "value_max": 10, + "value_min": 0, + "value_step": 1 + }, + { + "access": 3, + "description": "Motion detection distance", + "label": "Large motion detection distance", + "name": "large_motion_detection_distance", + "property": "large_motion_detection_distance", + "type": "numeric", + "unit": "m", + "value_max": 10, + "value_min": 0, + "value_step": 0.01 + }, + { + "access": 1, + "description": "State of the motion", + "label": "Motion state", + "name": "motion_state", + "property": "motion_state", + "type": "enum", + "values": [ + "none", + "small", + "medium", + "large" + ] + }, + { + "access": 3, + "description": "For how much time presence should stay true after detecting it", + "label": "Fading time", + "name": "fading_time", + "property": "fading_time", + "type": "numeric", + "unit": "s", + "value_max": 28800, + "value_min": 0, + "value_step": 1 + }, + { + "access": 3, + "description": "Medium motion detection distance", + "label": "Medium motion detection distance", + "name": "medium_motion_detection_distance", + "property": "medium_motion_detection_distance", + "type": "numeric", + "unit": "m", + "value_max": 6, + "value_min": 0, + "value_step": 0.01 + }, + { + "access": 3, + "description": "Medium motion detection sensitivity", + "label": "Medium motion detection sensitivity", + "name": "medium_motion_detection_sensitivity", + "property": "medium_motion_detection_sensitivity", + "type": "numeric", + "unit": "x", + "value_max": 10, + "value_min": 0, + "value_step": 1 + }, + { + "access": 3, + "description": "LED Indicator", + "label": "Indicator", + "name": "indicator", + "property": "indicator", + "type": "binary", + "value_off": "OFF", + "value_on": "ON" + }, + { + "access": 3, + "description": "Small detection distance", + "label": "Small detection distance", + "name": "small_detection_distance", + "property": "small_detection_distance", + "type": "numeric", + "unit": "m", + "value_max": 6, + "value_min": 0, + "value_step": 0.01 + }, + { + "access": 3, + "description": "Small detection sensitivity", + "label": "Small detection sensitivity", + "name": "small_detection_sensitivity", + "property": "small_detection_sensitivity", + "type": "numeric", + "unit": "x", + "value_max": 10, + "value_min": 0, + "value_step": 1 + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "ZG-205Z/A", + "options": [ + { + "access": 2, + "description": "Calibrates the illuminance value (percentual offset), takes into effect on next report of device.", + "label": "Illuminance calibration", + "name": "illuminance_calibration", + "property": "illuminance_calibration", + "type": "numeric" + } + ], + "supports_ota": false, + "vendor": "Tuya" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genBasic", + "genIdentify", + "ssIasZone", + "manuSpecificTuya_2", + "manuSpecificTuya", + "60928", + "57344", + "msIlluminanceMeasurement" + ], + "output": [] + }, + "configured_reportings": [], + "scenes": [] + }, + "242": { + "bindings": [], + "clusters": { + "input": [], + "output": [ + "greenPower" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Presence sensor", + "ieee_address": "0xa4c138c209c7b4de", + "interview_completed": true, + "interviewing": false, + "manufacturer": "_TZE200_2aaelwxk", + "model_id": "TS0225", + "network_address": 65189, + "power_source": "DC Source", + "software_build_id": "0122052017", + "supported": true, + "type": "Router" + }, + { + "date_code": "", + "definition": { + "description": "2 gang smart dimmer", + "exposes": [ + { + "endpoint": "l1", + "features": [ + { + "access": 3, + "description": "On/off state of this light", + "endpoint": "l1", + "label": "State", + "name": "state", + "property": "state_l1", + "type": "binary", + "value_off": "OFF", + "value_on": "ON", + "value_toggle": "TOGGLE" + }, + { + "access": 3, + "description": "Brightness of this light", + "endpoint": "l1", + "label": "Brightness", + "name": "brightness", + "property": "brightness_l1", + "type": "numeric", + "value_max": 254, + "value_min": 0 + }, + { + "access": 3, + "description": "Minimum light brightness", + "endpoint": "l1", + "label": "Min brightness", + "name": "min_brightness", + "property": "min_brightness_l1", + "type": "numeric", + "value_max": 255, + "value_min": 1 + }, + { + "access": 3, + "description": "Maximum light brightness", + "endpoint": "l1", + "label": "Max brightness", + "name": "max_brightness", + "property": "max_brightness_l1", + "type": "numeric", + "value_max": 255, + "value_min": 1 + } + ], + "type": "light" + }, + { + "access": 3, + "description": "Countdown to turn device off after a certain time", + "endpoint": "l1", + "label": "Countdown", + "name": "countdown", + "property": "countdown_l1", + "type": "numeric", + "unit": "s", + "value_max": 43200, + "value_min": 0, + "value_step": 1 + }, + { + "endpoint": "l2", + "features": [ + { + "access": 3, + "description": "On/off state of this light", + "endpoint": "l2", + "label": "State", + "name": "state", + "property": "state_l2", + "type": "binary", + "value_off": "OFF", + "value_on": "ON", + "value_toggle": "TOGGLE" + }, + { + "access": 3, + "description": "Brightness of this light", + "endpoint": "l2", + "label": "Brightness", + "name": "brightness", + "property": "brightness_l2", + "type": "numeric", + "value_max": 254, + "value_min": 0 + }, + { + "access": 3, + "description": "Minimum light brightness", + "endpoint": "l2", + "label": "Min brightness", + "name": "min_brightness", + "property": "min_brightness_l2", + "type": "numeric", + "value_max": 255, + "value_min": 1 + }, + { + "access": 3, + "description": "Maximum light brightness", + "endpoint": "l2", + "label": "Max brightness", + "name": "max_brightness", + "property": "max_brightness_l2", + "type": "numeric", + "value_max": 255, + "value_min": 1 + } + ], + "type": "light" + }, + { + "access": 3, + "description": "Countdown to turn device off after a certain time", + "endpoint": "l2", + "label": "Countdown", + "name": "countdown", + "property": "countdown_l2", + "type": "numeric", + "unit": "s", + "value_max": 43200, + "value_min": 0, + "value_step": 1 + }, + { + "access": 3, + "category": "config", + "description": "Controls the behavior when the device is powered on after power loss", + "label": "Power-on behavior", + "name": "power_on_behavior", + "property": "power_on_behavior", + "type": "enum", + "values": [ + "off", + "on", + "previous" + ] + }, + { + "access": 3, + "description": "Mode of the backlight", + "label": "Backlight mode", + "name": "backlight_mode", + "property": "backlight_mode", + "type": "enum", + "values": [ + "off", + "normal", + "inverted" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "TS0601_dimmer_2", + "options": [], + "supports_ota": false, + "vendor": "Tuya" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genBasic", + "genGroups", + "genScenes", + "manuSpecificTuya" + ], + "output": [ + "genOta", + "genTime" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Moes dimmer double", + "ieee_address": "0x847127fffeaff50a", + "interview_completed": true, + "interviewing": false, + "manufacturer": "_TZE200_e3oitdyu", + "model_id": "TS0601", + "network_address": 63077, + "power_source": "Mains (single phase)", + "supported": true, + "type": "Router" + }, + { + "date_code": "", + "definition": { + "description": "Air quality sensor", + "exposes": [ + { + "access": 1, + "description": "Measured temperature value", + "label": "Temperature", + "name": "temperature", + "property": "temperature", + "type": "numeric", + "unit": "°C" + }, + { + "access": 1, + "description": "Measured relative humidity", + "label": "Humidity", + "name": "humidity", + "property": "humidity", + "type": "numeric", + "unit": "%" + }, + { + "access": 1, + "description": "The measured CO2 (carbon dioxide) value", + "label": "CO2", + "name": "co2", + "property": "co2", + "type": "numeric", + "unit": "ppm" + }, + { + "access": 1, + "description": "Measured VOC value", + "label": "VOC", + "name": "voc", + "property": "voc", + "type": "numeric", + "unit": "ppm" + }, + { + "access": 1, + "description": "The measured formaldehyd value", + "label": "Formaldehyd", + "name": "formaldehyd", + "property": "formaldehyd", + "type": "numeric", + "unit": "mg/m³" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "TS0601_air_quality_sensor", + "options": [ + { + "access": 2, + "description": "Calibrates the temperature value (absolute offset), takes into effect on next report of device.", + "label": "Temperature calibration", + "name": "temperature_calibration", + "property": "temperature_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for temperature, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Temperature precision", + "name": "temperature_precision", + "property": "temperature_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the humidity value (absolute offset), takes into effect on next report of device.", + "label": "Humidity calibration", + "name": "humidity_calibration", + "property": "humidity_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for humidity, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Humidity precision", + "name": "humidity_precision", + "property": "humidity_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the co2 value (absolute offset), takes into effect on next report of device.", + "label": "Co2 calibration", + "name": "co2_calibration", + "property": "co2_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Calibrates the voc value (absolute offset), takes into effect on next report of device.", + "label": "Voc calibration", + "name": "voc_calibration", + "property": "voc_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Calibrates the formaldehyd value (absolute offset), takes into effect on next report of device.", + "label": "Formaldehyd calibration", + "name": "formaldehyd_calibration", + "property": "formaldehyd_calibration", + "type": "numeric" + } + ], + "supports_ota": false, + "vendor": "Tuya" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genGroups", + "genScenes", + "manuSpecificTuya", + "genBasic" + ], + "output": [ + "genOta", + "genTime" + ] + }, + "configured_reportings": [], + "scenes": [] + }, + "242": { + "bindings": [], + "clusters": { + "input": [], + "output": [ + "greenPower" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Smart air box 2 (_TZE200_mja3fuja)", + "ieee_address": "0xa4c1381b458a63b3", + "interview_completed": true, + "interviewing": false, + "manufacturer": "_TZE200_mja3fuja", + "model_id": "TS0601", + "network_address": 40673, + "power_source": "Mains (single phase)", + "supported": true, + "type": "Router" + }, + { + "date_code": "", + "definition": { + "description": "Smart circuit breaker", + "exposes": [ + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + }, + { + "access": 1, + "description": "Measured temperature value", + "label": "Temperature", + "name": "temperature", + "property": "temperature", + "type": "numeric", + "unit": "°C" + }, + { + "access": 3, + "description": "High temperature threshold", + "label": "Temperature threshold", + "name": "temperature_threshold", + "property": "temperature_threshold", + "type": "numeric", + "unit": "*C", + "value_max": 100, + "value_min": 40, + "value_step": 1 + }, + { + "access": 3, + "description": "High temperature breaker", + "label": "Temperature breaker", + "name": "temperature_breaker", + "property": "temperature_breaker", + "type": "binary", + "value_off": "OFF", + "value_on": "ON" + }, + { + "access": 3, + "description": "High power threshold", + "label": "Power threshold", + "name": "power_threshold", + "property": "power_threshold", + "type": "numeric", + "unit": "kW", + "value_max": 26, + "value_min": 1, + "value_step": 1 + }, + { + "access": 3, + "description": "High power breaker", + "label": "Power breaker", + "name": "power_breaker", + "property": "power_breaker", + "type": "binary", + "value_off": "OFF", + "value_on": "ON" + }, + { + "access": 3, + "description": "Over-current threshold", + "label": "Over current threshold", + "name": "over_current_threshold", + "property": "over_current_threshold", + "type": "numeric", + "unit": "A", + "value_max": 64, + "value_min": 1, + "value_step": 1 + }, + { + "access": 3, + "description": "Over-current breaker", + "label": "Over current breaker", + "name": "over_current_breaker", + "property": "over_current_breaker", + "type": "binary", + "value_off": "OFF", + "value_on": "ON" + }, + { + "access": 3, + "description": "Over-voltage threshold", + "label": "Over voltage threshold", + "name": "over_voltage_threshold", + "property": "over_voltage_threshold", + "type": "numeric", + "unit": "V", + "value_max": 265, + "value_min": 220, + "value_step": 1 + }, + { + "access": 3, + "description": "Over-voltage breaker", + "label": "Over voltage breaker", + "name": "over_voltage_breaker", + "property": "over_voltage_breaker", + "type": "binary", + "value_off": "OFF", + "value_on": "ON" + }, + { + "access": 3, + "description": "Under-voltage threshold", + "label": "Under voltage threshold", + "name": "under_voltage_threshold", + "property": "under_voltage_threshold", + "type": "numeric", + "unit": "V", + "value_max": 240, + "value_min": 76, + "value_step": 1 + }, + { + "access": 3, + "description": "Under-voltage breaker", + "label": "Under voltage breaker", + "name": "under_voltage_breaker", + "property": "under_voltage_breaker", + "type": "binary", + "value_off": "OFF", + "value_on": "ON" + }, + { + "features": [ + { + "access": 7, + "description": "On/off state of the switch", + "label": "State", + "name": "state", + "property": "state", + "type": "binary", + "value_off": "OFF", + "value_on": "ON", + "value_toggle": "TOGGLE" + } + ], + "type": "switch" + }, + { + "access": 7, + "description": "Recover state after power outage", + "label": "Power outage memory", + "name": "power_outage_memory", + "property": "power_outage_memory", + "type": "enum", + "values": [ + "on", + "off", + "restore" + ] + }, + { + "access": 7, + "description": "LED indicator mode", + "label": "Indicator mode", + "name": "indicator_mode", + "property": "indicator_mode", + "type": "enum", + "values": [ + "off", + "off/on", + "on/off", + "on" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Instantaneous measured power", + "label": "Power", + "name": "power", + "property": "power", + "type": "numeric", + "unit": "W" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Instantaneous measured electrical current", + "label": "Current", + "name": "current", + "property": "current", + "type": "numeric", + "unit": "A" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Measured electrical potential value", + "label": "Voltage", + "name": "voltage", + "property": "voltage", + "type": "numeric", + "unit": "V" + }, + { + "access": 1, + "description": "Sum of consumed energy", + "label": "Energy", + "name": "energy", + "property": "energy", + "type": "numeric", + "unit": "kWh" + } + ], + "model": "EAKCB-T-M-Z", + "options": [ + { + "access": 2, + "description": "Calibrates the temperature value (absolute offset), takes into effect on next report of device.", + "label": "Temperature calibration", + "name": "temperature_calibration", + "property": "temperature_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for temperature, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Temperature precision", + "name": "temperature_precision", + "property": "temperature_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the power value (percentual offset), takes into effect on next report of device.", + "label": "Power calibration", + "name": "power_calibration", + "property": "power_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for power, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Power precision", + "name": "power_precision", + "property": "power_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the current value (percentual offset), takes into effect on next report of device.", + "label": "Current calibration", + "name": "current_calibration", + "property": "current_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for current, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Current precision", + "name": "current_precision", + "property": "current_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the voltage value (percentual offset), takes into effect on next report of device.", + "label": "Voltage calibration", + "name": "voltage_calibration", + "property": "voltage_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for voltage, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Voltage precision", + "name": "voltage_precision", + "property": "voltage_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the energy value (percentual offset), takes into effect on next report of device.", + "label": "Energy calibration", + "name": "energy_calibration", + "property": "energy_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for energy, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Energy precision", + "name": "energy_precision", + "property": "energy_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "State actions will also be published as 'action' when true (default false).", + "label": "State action", + "name": "state_action", + "property": "state_action", + "type": "binary", + "value_off": false, + "value_on": true + } + ], + "supports_ota": true, + "vendor": "EARU" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [ + { + "cluster": "msTemperatureMeasurement", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + }, + { + "cluster": "genOnOff", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + }, + { + "cluster": "haElectricalMeasurement", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + }, + { + "cluster": "seMetering", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + } + ], + "clusters": { + "input": [ + "genBasic", + "genIdentify", + "genGroups", + "genScenes", + "genOnOff", + "seMetering", + "haElectricalMeasurement", + "msTemperatureMeasurement", + "57344", + "manuSpecificTuya_3" + ], + "output": [ + "genOta", + "genTime" + ] + }, + "configured_reportings": [ + { + "attribute": "rmsVoltage", + "cluster": "haElectricalMeasurement", + "maximum_report_interval": 3600, + "minimum_report_interval": 5, + "reportable_change": 5 + }, + { + "attribute": "rmsCurrent", + "cluster": "haElectricalMeasurement", + "maximum_report_interval": 3600, + "minimum_report_interval": 5, + "reportable_change": 50 + }, + { + "attribute": "activePower", + "cluster": "haElectricalMeasurement", + "maximum_report_interval": 3600, + "minimum_report_interval": 5, + "reportable_change": 10 + }, + { + "attribute": "currentSummDelivered", + "cluster": "seMetering", + "maximum_report_interval": 3600, + "minimum_report_interval": 5, + "reportable_change": [ + 1, + 1 + ] + } + ], + "scenes": [] + }, + "242": { + "bindings": [], + "clusters": { + "input": [], + "output": [ + "greenPower" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "EARU Circuit Breaker 16A", + "ieee_address": "0xdc8e95fffe3f2eed", + "interview_completed": true, + "interviewing": false, + "manufacturer": "_TZ3000_lepzuhto", + "model_id": "TS011F", + "network_address": 50272, + "power_source": "Mains (single phase)", + "supported": true, + "type": "Router" + }, + { + "date_code": "", + "definition": { + "description": "Smart circuit breaker", + "exposes": [ + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + }, + { + "access": 1, + "description": "Measured temperature value", + "label": "Temperature", + "name": "temperature", + "property": "temperature", + "type": "numeric", + "unit": "°C" + }, + { + "access": 3, + "description": "High temperature threshold", + "label": "Temperature threshold", + "name": "temperature_threshold", + "property": "temperature_threshold", + "type": "numeric", + "unit": "*C", + "value_max": 100, + "value_min": 40, + "value_step": 1 + }, + { + "access": 3, + "description": "High temperature breaker", + "label": "Temperature breaker", + "name": "temperature_breaker", + "property": "temperature_breaker", + "type": "binary", + "value_off": "OFF", + "value_on": "ON" + }, + { + "access": 3, + "description": "High power threshold", + "label": "Power threshold", + "name": "power_threshold", + "property": "power_threshold", + "type": "numeric", + "unit": "kW", + "value_max": 26, + "value_min": 1, + "value_step": 1 + }, + { + "access": 3, + "description": "High power breaker", + "label": "Power breaker", + "name": "power_breaker", + "property": "power_breaker", + "type": "binary", + "value_off": "OFF", + "value_on": "ON" + }, + { + "access": 3, + "description": "Over-current threshold", + "label": "Over current threshold", + "name": "over_current_threshold", + "property": "over_current_threshold", + "type": "numeric", + "unit": "A", + "value_max": 64, + "value_min": 1, + "value_step": 1 + }, + { + "access": 3, + "description": "Over-current breaker", + "label": "Over current breaker", + "name": "over_current_breaker", + "property": "over_current_breaker", + "type": "binary", + "value_off": "OFF", + "value_on": "ON" + }, + { + "access": 3, + "description": "Over-voltage threshold", + "label": "Over voltage threshold", + "name": "over_voltage_threshold", + "property": "over_voltage_threshold", + "type": "numeric", + "unit": "V", + "value_max": 265, + "value_min": 220, + "value_step": 1 + }, + { + "access": 3, + "description": "Over-voltage breaker", + "label": "Over voltage breaker", + "name": "over_voltage_breaker", + "property": "over_voltage_breaker", + "type": "binary", + "value_off": "OFF", + "value_on": "ON" + }, + { + "access": 3, + "description": "Under-voltage threshold", + "label": "Under voltage threshold", + "name": "under_voltage_threshold", + "property": "under_voltage_threshold", + "type": "numeric", + "unit": "V", + "value_max": 240, + "value_min": 76, + "value_step": 1 + }, + { + "access": 3, + "description": "Under-voltage breaker", + "label": "Under voltage breaker", + "name": "under_voltage_breaker", + "property": "under_voltage_breaker", + "type": "binary", + "value_off": "OFF", + "value_on": "ON" + }, + { + "features": [ + { + "access": 7, + "description": "On/off state of the switch", + "label": "State", + "name": "state", + "property": "state", + "type": "binary", + "value_off": "OFF", + "value_on": "ON", + "value_toggle": "TOGGLE" + } + ], + "type": "switch" + }, + { + "access": 7, + "description": "Recover state after power outage", + "label": "Power outage memory", + "name": "power_outage_memory", + "property": "power_outage_memory", + "type": "enum", + "values": [ + "on", + "off", + "restore" + ] + }, + { + "access": 7, + "description": "LED indicator mode", + "label": "Indicator mode", + "name": "indicator_mode", + "property": "indicator_mode", + "type": "enum", + "values": [ + "off", + "off/on", + "on/off", + "on" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Instantaneous measured power", + "label": "Power", + "name": "power", + "property": "power", + "type": "numeric", + "unit": "W" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Instantaneous measured electrical current", + "label": "Current", + "name": "current", + "property": "current", + "type": "numeric", + "unit": "A" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Measured electrical potential value", + "label": "Voltage", + "name": "voltage", + "property": "voltage", + "type": "numeric", + "unit": "V" + }, + { + "access": 1, + "description": "Sum of consumed energy", + "label": "Energy", + "name": "energy", + "property": "energy", + "type": "numeric", + "unit": "kWh" + } + ], + "model": "EAKCB-T-M-Z", + "options": [ + { + "access": 2, + "description": "Calibrates the temperature value (absolute offset), takes into effect on next report of device.", + "label": "Temperature calibration", + "name": "temperature_calibration", + "property": "temperature_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for temperature, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Temperature precision", + "name": "temperature_precision", + "property": "temperature_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the power value (percentual offset), takes into effect on next report of device.", + "label": "Power calibration", + "name": "power_calibration", + "property": "power_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for power, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Power precision", + "name": "power_precision", + "property": "power_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the current value (percentual offset), takes into effect on next report of device.", + "label": "Current calibration", + "name": "current_calibration", + "property": "current_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for current, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Current precision", + "name": "current_precision", + "property": "current_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the voltage value (percentual offset), takes into effect on next report of device.", + "label": "Voltage calibration", + "name": "voltage_calibration", + "property": "voltage_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for voltage, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Voltage precision", + "name": "voltage_precision", + "property": "voltage_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the energy value (percentual offset), takes into effect on next report of device.", + "label": "Energy calibration", + "name": "energy_calibration", + "property": "energy_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for energy, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Energy precision", + "name": "energy_precision", + "property": "energy_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "State actions will also be published as 'action' when true (default false).", + "label": "State action", + "name": "state_action", + "property": "state_action", + "type": "binary", + "value_off": false, + "value_on": true + } + ], + "supports_ota": true, + "vendor": "EARU" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [ + { + "cluster": "msTemperatureMeasurement", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + }, + { + "cluster": "genOnOff", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + }, + { + "cluster": "haElectricalMeasurement", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + }, + { + "cluster": "seMetering", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + } + ], + "clusters": { + "input": [ + "genBasic", + "genIdentify", + "genGroups", + "genScenes", + "genOnOff", + "seMetering", + "haElectricalMeasurement", + "msTemperatureMeasurement", + "57344", + "manuSpecificTuya_3" + ], + "output": [ + "genOta", + "genTime" + ] + }, + "configured_reportings": [ + { + "attribute": "rmsVoltage", + "cluster": "haElectricalMeasurement", + "maximum_report_interval": 3600, + "minimum_report_interval": 5, + "reportable_change": 5 + }, + { + "attribute": "rmsCurrent", + "cluster": "haElectricalMeasurement", + "maximum_report_interval": 3600, + "minimum_report_interval": 5, + "reportable_change": 50 + }, + { + "attribute": "activePower", + "cluster": "haElectricalMeasurement", + "maximum_report_interval": 3600, + "minimum_report_interval": 5, + "reportable_change": 10 + }, + { + "attribute": "currentSummDelivered", + "cluster": "seMetering", + "maximum_report_interval": 3600, + "minimum_report_interval": 5, + "reportable_change": [ + 1, + 1 + ] + } + ], + "scenes": [] + }, + "242": { + "bindings": [], + "clusters": { + "input": [], + "output": [ + "greenPower" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "EARU Circuit Breaker 32A", + "ieee_address": "0xdc8e95fffe3f4853", + "interview_completed": true, + "interviewing": false, + "manufacturer": "_TZ3000_lepzuhto", + "model_id": "TS011F", + "network_address": 7298, + "power_source": "Mains (single phase)", + "supported": true, + "type": "Router" + }, + { + "date_code": "20221205", + "definition": { + "description": "Zigbee GU10 LED lamp", + "exposes": [ + { + "features": [ + { + "access": 7, + "description": "On/off state of this light", + "label": "State", + "name": "state", + "property": "state", + "type": "binary", + "value_off": "OFF", + "value_on": "ON", + "value_toggle": "TOGGLE" + }, + { + "access": 7, + "description": "Brightness of this light", + "label": "Brightness", + "name": "brightness", + "property": "brightness", + "type": "numeric", + "value_max": 254, + "value_min": 0 + }, + { + "access": 7, + "description": "Color temperature of this light", + "label": "Color temp", + "name": "color_temp", + "presets": [ + { + "description": "Coolest temperature supported", + "name": "coolest", + "value": 158 + }, + { + "description": "Cool temperature (250 mireds / 4000 Kelvin)", + "name": "cool", + "value": 250 + }, + { + "description": "Neutral temperature (370 mireds / 2700 Kelvin)", + "name": "neutral", + "value": 370 + }, + { + "description": "Warm temperature (454 mireds / 2200 Kelvin)", + "name": "warm", + "value": 454 + }, + { + "description": "Warmest temperature supported", + "name": "warmest", + "value": 495 + } + ], + "property": "color_temp", + "type": "numeric", + "unit": "mired", + "value_max": 495, + "value_min": 158 + }, + { + "access": 7, + "description": "Color temperature after cold power on of this light", + "label": "Color temp startup", + "name": "color_temp_startup", + "presets": [ + { + "description": "Coolest temperature supported", + "name": "coolest", + "value": 158 + }, + { + "description": "Cool temperature (250 mireds / 4000 Kelvin)", + "name": "cool", + "value": 250 + }, + { + "description": "Neutral temperature (370 mireds / 2700 Kelvin)", + "name": "neutral", + "value": 370 + }, + { + "description": "Warm temperature (454 mireds / 2200 Kelvin)", + "name": "warm", + "value": 454 + }, + { + "description": "Warmest temperature supported", + "name": "warmest", + "value": 495 + }, + { + "description": "Restore previous color_temp on cold power on", + "name": "previous", + "value": 65535 + } + ], + "property": "color_temp_startup", + "type": "numeric", + "unit": "mired", + "value_max": 495, + "value_min": 158 + }, + { + "access": 7, + "description": "Color of this light in the CIE 1931 color space (x/y)", + "features": [ + { + "access": 7, + "label": "X", + "name": "x", + "property": "x", + "type": "numeric" + }, + { + "access": 7, + "label": "Y", + "name": "y", + "property": "y", + "type": "numeric" + } + ], + "label": "Color (X/Y)", + "name": "color_xy", + "property": "color", + "type": "composite" + }, + { + "access": 7, + "description": "Color of this light expressed as hue/saturation", + "features": [ + { + "access": 7, + "label": "Hue", + "name": "hue", + "property": "hue", + "type": "numeric" + }, + { + "access": 7, + "label": "Saturation", + "name": "saturation", + "property": "saturation", + "type": "numeric" + } + ], + "label": "Color (HS)", + "name": "color_hs", + "property": "color", + "type": "composite" + } + ], + "type": "light" + }, + { + "access": 2, + "description": "Triggers an effect on the light (e.g. make light blink for a few seconds)", + "label": "Effect", + "name": "effect", + "property": "effect", + "type": "enum", + "values": [ + "blink", + "breathe", + "okay", + "channel_change", + "finish_effect", + "stop_effect", + "colorloop", + "stop_colorloop" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "GL-S-006P", + "options": [ + { + "access": 2, + "description": "Controls the transition time (in seconds) of on/off, brightness, color temperature (if applicable) and color (if applicable) changes. Defaults to `0` (no transition).", + "label": "Transition", + "name": "transition", + "property": "transition", + "type": "numeric", + "value_min": 0 + }, + { + "access": 2, + "description": "When enabled colors will be synced, e.g. if the light supports both color x/y and color temperature a conversion from color x/y to color temperature will be done when setting the x/y color (default true).", + "label": "Color sync", + "name": "color_sync", + "property": "color_sync", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 2, + "description": "State actions will also be published as 'action' when true (default false).", + "label": "State action", + "name": "state_action", + "property": "state_action", + "type": "binary", + "value_off": false, + "value_on": true + } + ], + "supports_ota": true, + "vendor": "Gledopto" + }, + "disabled": false, + "endpoints": { + "11": { + "bindings": [ + { + "cluster": "genOnOff", + "target": { + "endpoint": 1, + "ieee_address": "0x00124b0025e1f196", + "type": "endpoint" + } + } + ], + "clusters": { + "input": [ + "genBasic", + "genIdentify", + "genGroups", + "genScenes", + "genOnOff", + "genLevelCtrl", + "lightingColorCtrl", + "touchlink" + ], + "output": [ + "genOta" + ] + }, + "configured_reportings": [ + { + "attribute": "onOff", + "cluster": "genOnOff", + "maximum_report_interval": 3600, + "minimum_report_interval": 60, + "reportable_change": 0 + } + ], + "scenes": [] + }, + "242": { + "bindings": [], + "clusters": { + "input": [], + "output": [ + "greenPower" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Gledopto RGBCTT light II", + "ieee_address": "0xa4c13876c0fe9c0d", + "interview_completed": true, + "interviewing": false, + "manufacturer": "GLEDOPTO", + "model_id": "GL-S-006P", + "network_address": 11259, + "power_source": "Mains (single phase)", + "software_build_id": "10951203", + "supported": true, + "type": "Router" + }, + { + "date_code": "", + "definition": { + "description": "Air quality sensor", + "exposes": [ + { + "access": 1, + "description": "Measured temperature value", + "label": "Temperature", + "name": "temperature", + "property": "temperature", + "type": "numeric", + "unit": "°C" + }, + { + "access": 1, + "description": "Measured relative humidity", + "label": "Humidity", + "name": "humidity", + "property": "humidity", + "type": "numeric", + "unit": "%" + }, + { + "access": 1, + "description": "The measured CO2 (carbon dioxide) value", + "label": "CO2", + "name": "co2", + "property": "co2", + "type": "numeric", + "unit": "ppm" + }, + { + "access": 1, + "description": "Measured VOC value", + "label": "VOC", + "name": "voc", + "property": "voc", + "type": "numeric", + "unit": "ppm" + }, + { + "access": 1, + "description": "The measured formaldehyd value", + "label": "Formaldehyd", + "name": "formaldehyd", + "property": "formaldehyd", + "type": "numeric", + "unit": "mg/m³" + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "TS0601_air_quality_sensor", + "options": [ + { + "access": 2, + "description": "Calibrates the temperature value (absolute offset), takes into effect on next report of device.", + "label": "Temperature calibration", + "name": "temperature_calibration", + "property": "temperature_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for temperature, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Temperature precision", + "name": "temperature_precision", + "property": "temperature_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the humidity value (absolute offset), takes into effect on next report of device.", + "label": "Humidity calibration", + "name": "humidity_calibration", + "property": "humidity_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Number of digits after decimal point for humidity, takes into effect on next report of device. This option can only decrease the precision, not increase it.", + "label": "Humidity precision", + "name": "humidity_precision", + "property": "humidity_precision", + "type": "numeric", + "value_max": 3, + "value_min": 0 + }, + { + "access": 2, + "description": "Calibrates the co2 value (absolute offset), takes into effect on next report of device.", + "label": "Co2 calibration", + "name": "co2_calibration", + "property": "co2_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Calibrates the voc value (absolute offset), takes into effect on next report of device.", + "label": "Voc calibration", + "name": "voc_calibration", + "property": "voc_calibration", + "type": "numeric" + }, + { + "access": 2, + "description": "Calibrates the formaldehyd value (absolute offset), takes into effect on next report of device.", + "label": "Formaldehyd calibration", + "name": "formaldehyd_calibration", + "property": "formaldehyd_calibration", + "type": "numeric" + } + ], + "supports_ota": false, + "vendor": "Tuya" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genGroups", + "genScenes", + "manuSpecificTuya", + "genBasic" + ], + "output": [ + "genOta", + "genTime" + ] + }, + "configured_reportings": [], + "scenes": [] + }, + "242": { + "bindings": [], + "clusters": { + "input": [], + "output": [ + "greenPower" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Tuya Air Quality Sensor (_TZE200_yvx5lh6k)", + "ieee_address": "0xa4c138479d307a4b", + "interview_completed": true, + "interviewing": false, + "manufacturer": "_TZE200_yvx5lh6k", + "model_id": "TS0601", + "network_address": 40313, + "power_source": "Mains (single phase)", + "supported": true, + "type": "Router" + }, + { + "date_code": "20220523", + "definition": { + "description": "Zigbee triac AC dimmer", + "exposes": [ + { + "features": [ + { + "access": 7, + "description": "On/off state of this light", + "label": "State", + "name": "state", + "property": "state", + "type": "binary", + "value_off": "OFF", + "value_on": "ON", + "value_toggle": "TOGGLE" + }, + { + "access": 7, + "description": "Brightness of this light", + "label": "Brightness", + "name": "brightness", + "property": "brightness", + "type": "numeric", + "value_max": 254, + "value_min": 0 + } + ], + "type": "light" + }, + { + "access": 2, + "description": "Triggers an effect on the light (e.g. make light blink for a few seconds)", + "label": "Effect", + "name": "effect", + "property": "effect", + "type": "enum", + "values": [ + "blink", + "breathe", + "okay", + "channel_change", + "finish_effect", + "stop_effect" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "GL-SD-001", + "options": [ + { + "access": 2, + "description": "Controls the transition time (in seconds) of on/off, brightness, color temperature (if applicable) and color (if applicable) changes. Defaults to `0` (no transition).", + "label": "Transition", + "name": "transition", + "property": "transition", + "type": "numeric", + "value_min": 0 + }, + { + "access": 2, + "description": "State actions will also be published as 'action' when true (default false).", + "label": "State action", + "name": "state_action", + "property": "state_action", + "type": "binary", + "value_off": false, + "value_on": true + } + ], + "supports_ota": false, + "vendor": "Gledopto" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genBasic", + "genIdentify", + "genGroups", + "genScenes", + "genOnOff", + "genLevelCtrl", + "touchlink" + ], + "output": [ + "genOta" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Gledopto GL-SD-001", + "ieee_address": "0x04cd15fffeddccd4", + "interview_completed": true, + "interviewing": false, + "manufacturer": "GLEDOPTO", + "model_id": "GL-SD-001", + "network_address": 47837, + "power_source": "Mains (single phase)", + "software_build_id": "10551201", + "supported": true, + "type": "Router" + }, + { + "date_code": "", + "definition": { + "description": "RGB+CCT 4.7W GU10 LED bulb", + "exposes": [ + { + "features": [ + { + "access": 7, + "description": "On/off state of this light", + "label": "State", + "name": "state", + "property": "state", + "type": "binary", + "value_off": "OFF", + "value_on": "ON", + "value_toggle": "TOGGLE" + }, + { + "access": 7, + "description": "Brightness of this light", + "label": "Brightness", + "name": "brightness", + "property": "brightness", + "type": "numeric", + "value_max": 254, + "value_min": 0 + }, + { + "access": 7, + "description": "Color temperature of this light", + "label": "Color temp", + "name": "color_temp", + "presets": [ + { + "description": "Coolest temperature supported", + "name": "coolest", + "value": 153 + }, + { + "description": "Cool temperature (250 mireds / 4000 Kelvin)", + "name": "cool", + "value": 250 + }, + { + "description": "Neutral temperature (370 mireds / 2700 Kelvin)", + "name": "neutral", + "value": 370 + }, + { + "description": "Warm temperature (454 mireds / 2200 Kelvin)", + "name": "warm", + "value": 454 + }, + { + "description": "Warmest temperature supported", + "name": "warmest", + "value": 500 + } + ], + "property": "color_temp", + "type": "numeric", + "unit": "mired", + "value_max": 500, + "value_min": 153 + }, + { + "access": 7, + "description": "Color of this light in the CIE 1931 color space (x/y)", + "features": [ + { + "access": 7, + "label": "X", + "name": "x", + "property": "x", + "type": "numeric" + }, + { + "access": 7, + "label": "Y", + "name": "y", + "property": "y", + "type": "numeric" + } + ], + "label": "Color (X/Y)", + "name": "color_xy", + "property": "color", + "type": "composite" + } + ], + "type": "light" + }, + { + "access": 2, + "description": "Triggers an effect on the light (e.g. make light blink for a few seconds)", + "label": "Effect", + "name": "effect", + "property": "effect", + "type": "enum", + "values": [ + "blink", + "breathe", + "okay", + "channel_change", + "finish_effect", + "stop_effect", + "colorloop", + "stop_colorloop" + ] + }, + { + "access": 3, + "description": "Do not disturb mode, when enabled this function will keep the light OFF after a power outage", + "label": "Do not disturb", + "name": "do_not_disturb", + "property": "do_not_disturb", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 3, + "description": "Power on behavior state", + "label": "Color power on behavior", + "name": "color_power_on_behavior", + "property": "color_power_on_behavior", + "type": "enum", + "values": [ + "initial", + "previous", + "customized" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "ZB-TD5-RCW-GU10", + "options": [ + { + "access": 2, + "description": "Controls the transition time (in seconds) of on/off, brightness, color temperature (if applicable) and color (if applicable) changes. Defaults to `0` (no transition).", + "label": "Transition", + "name": "transition", + "property": "transition", + "type": "numeric", + "value_min": 0 + }, + { + "access": 2, + "description": "When enabled colors will be synced, e.g. if the light supports both color x/y and color temperature a conversion from color x/y to color temperature will be done when setting the x/y color (default true).", + "label": "Color sync", + "name": "color_sync", + "property": "color_sync", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 2, + "description": "State actions will also be published as 'action' when true (default false).", + "label": "State action", + "name": "state_action", + "property": "state_action", + "type": "binary", + "value_off": false, + "value_on": true + } + ], + "supports_ota": false, + "vendor": "Moes" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genIdentify", + "genGroups", + "genScenes", + "genOnOff", + "touchlink", + "genLevelCtrl", + "lightingColorCtrl", + "manuSpecificTuya", + "genBasic" + ], + "output": [ + "genOta", + "genTime" + ] + }, + "configured_reportings": [], + "scenes": [] + }, + "242": { + "bindings": [], + "clusters": { + "input": [], + "output": [ + "greenPower" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Moes RGBCCT light bulb III", + "ieee_address": "0xa4c138e0a08604f5", + "interview_completed": true, + "interviewing": false, + "manufacturer": "_TZ3210_rcggc0ys", + "model_id": "TS0505B", + "network_address": 50985, + "power_source": "Mains (single phase)", + "supported": true, + "type": "Router" + }, + { + "date_code": "", + "definition": { + "description": "RGB+CCT 4.7W GU10 LED bulb", + "exposes": [ + { + "features": [ + { + "access": 7, + "description": "On/off state of this light", + "label": "State", + "name": "state", + "property": "state", + "type": "binary", + "value_off": "OFF", + "value_on": "ON", + "value_toggle": "TOGGLE" + }, + { + "access": 7, + "description": "Brightness of this light", + "label": "Brightness", + "name": "brightness", + "property": "brightness", + "type": "numeric", + "value_max": 254, + "value_min": 0 + }, + { + "access": 7, + "description": "Color temperature of this light", + "label": "Color temp", + "name": "color_temp", + "presets": [ + { + "description": "Coolest temperature supported", + "name": "coolest", + "value": 153 + }, + { + "description": "Cool temperature (250 mireds / 4000 Kelvin)", + "name": "cool", + "value": 250 + }, + { + "description": "Neutral temperature (370 mireds / 2700 Kelvin)", + "name": "neutral", + "value": 370 + }, + { + "description": "Warm temperature (454 mireds / 2200 Kelvin)", + "name": "warm", + "value": 454 + }, + { + "description": "Warmest temperature supported", + "name": "warmest", + "value": 500 + } + ], + "property": "color_temp", + "type": "numeric", + "unit": "mired", + "value_max": 500, + "value_min": 153 + }, + { + "access": 7, + "description": "Color of this light in the CIE 1931 color space (x/y)", + "features": [ + { + "access": 7, + "label": "X", + "name": "x", + "property": "x", + "type": "numeric" + }, + { + "access": 7, + "label": "Y", + "name": "y", + "property": "y", + "type": "numeric" + } + ], + "label": "Color (X/Y)", + "name": "color_xy", + "property": "color", + "type": "composite" + } + ], + "type": "light" + }, + { + "access": 2, + "description": "Triggers an effect on the light (e.g. make light blink for a few seconds)", + "label": "Effect", + "name": "effect", + "property": "effect", + "type": "enum", + "values": [ + "blink", + "breathe", + "okay", + "channel_change", + "finish_effect", + "stop_effect", + "colorloop", + "stop_colorloop" + ] + }, + { + "access": 3, + "description": "Do not disturb mode, when enabled this function will keep the light OFF after a power outage", + "label": "Do not disturb", + "name": "do_not_disturb", + "property": "do_not_disturb", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 3, + "description": "Power on behavior state", + "label": "Color power on behavior", + "name": "color_power_on_behavior", + "property": "color_power_on_behavior", + "type": "enum", + "values": [ + "initial", + "previous", + "customized" + ] + }, + { + "access": 1, + "category": "diagnostic", + "description": "Link quality (signal strength)", + "label": "Linkquality", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "ZB-TD5-RCW-GU10", + "options": [ + { + "access": 2, + "description": "Controls the transition time (in seconds) of on/off, brightness, color temperature (if applicable) and color (if applicable) changes. Defaults to `0` (no transition).", + "label": "Transition", + "name": "transition", + "property": "transition", + "type": "numeric", + "value_min": 0 + }, + { + "access": 2, + "description": "When enabled colors will be synced, e.g. if the light supports both color x/y and color temperature a conversion from color x/y to color temperature will be done when setting the x/y color (default true).", + "label": "Color sync", + "name": "color_sync", + "property": "color_sync", + "type": "binary", + "value_off": false, + "value_on": true + }, + { + "access": 2, + "description": "State actions will also be published as 'action' when true (default false).", + "label": "State action", + "name": "state_action", + "property": "state_action", + "type": "binary", + "value_off": false, + "value_on": true + } + ], + "supports_ota": false, + "vendor": "Moes" + }, + "disabled": false, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genIdentify", + "genGroups", + "genScenes", + "genOnOff", + "touchlink", + "genLevelCtrl", + "lightingColorCtrl", + "manuSpecificTuya", + "genBasic" + ], + "output": [ + "genOta", + "genTime" + ] + }, + "configured_reportings": [], + "scenes": [] + }, + "242": { + "bindings": [], + "clusters": { + "input": [], + "output": [ + "greenPower" + ] + }, + "configured_reportings": [], + "scenes": [] + } + }, + "friendly_name": "Moes RGBCCT light bulb II", + "ieee_address": "0xa4c138a2a00bb8d4", + "interview_completed": true, + "interviewing": false, + "manufacturer": "_TZ3210_rcggc0ys", + "model_id": "TS0505B", + "network_address": 11468, + "power_source": "Mains (single phase)", + "supported": true, + "type": "Router" + } +] \ No newline at end of file diff --git a/src/mock/bridge-groups.json b/src/mock/bridge-groups.json new file mode 100644 index 0000000..7b5ef14 --- /dev/null +++ b/src/mock/bridge-groups.json @@ -0,0 +1,123 @@ +[ + { + "friendly_name": "At home", + "id": 1, + "members": [], + "scenes": [] + }, + { + "friendly_name": "Is dark", + "id": 2, + "members": [], + "scenes": [] + }, + { + "friendly_name": "Is night", + "id": 3, + "members": [], + "scenes": [] + }, + { + "friendly_name": "Sleeping", + "id": 4, + "members": [], + "scenes": [] + }, + { + "friendly_name": "Guests", + "id": 5, + "members": [], + "scenes": [] + }, + { + "friendly_name": "Master motion", + "id": 6, + "members": [], + "scenes": [] + }, + { + "friendly_name": "Lights", + "id": 7, + "members": [ + { + "endpoint": 1, + "ieee_address": "0xa4c138363dff5c1d" + }, + { + "endpoint": 11, + "ieee_address": "0xa4c138071003495f" + }, + { + "endpoint": 1, + "ieee_address": "0x04cd15fffe0d0ebd" + }, + { + "endpoint": 1, + "ieee_address": "0xa4c1388ad0ebb0a6" + }, + { + "endpoint": 1, + "ieee_address": "0xa4c138a2a00bb8d4" + }, + { + "endpoint": 1, + "ieee_address": "0xa4c138e0a08604f5" + } + ], + "scenes": [] + }, + { + "friendly_name": "Switches", + "id": 8, + "members": [ + { + "endpoint": 1, + "ieee_address": "0x54ef44100086d51f" + }, + { + "endpoint": 1, + "ieee_address": "0x54ef4410009716a8" + }, + { + "endpoint": 2, + "ieee_address": "0x54ef4410009716a8" + }, + { + "endpoint": 2, + "ieee_address": "0xcc86ecfffe4e9d25" + }, + { + "endpoint": 1, + "ieee_address": "0xcc86ecfffe4e9d25" + } + ], + "scenes": [] + }, + { + "friendly_name": "Covers", + "id": 9, + "members": [ + { + "endpoint": 1, + "ieee_address": "0x70ac08fffee6dd5e" + } + ], + "scenes": [] + }, + { + "friendly_name": "Thermostats", + "id": 10, + "members": [ + { + "endpoint": 1, + "ieee_address": "0xa4c138f402dfba8a" + } + ], + "scenes": [ + { + "id": 0, + "name": "Heat 37" + } + ] + } +] \ No newline at end of file diff --git a/src/mock/bridge-info.json b/src/mock/bridge-info.json new file mode 100644 index 0000000..ada54ef --- /dev/null +++ b/src/mock/bridge-info.json @@ -0,0 +1,1417 @@ +{ + "commit": "69f7282", + "config": { + "advanced": { + "availability_blacklist": [], + "availability_blocklist": [], + "availability_passlist": [], + "availability_whitelist": [], + "cache_state": true, + "cache_state_persistent": true, + "cache_state_send_on_startup": true, + "channel": 20, + "elapsed": false, + "ext_pan_id": [ + 61, + 200, + 45, + 154, + 163, + 42, + 44, + 146 + ], + "homeassistant_legacy_entity_attributes": false, + "last_seen": "ISO_8601_local", + "legacy_api": false, + "legacy_availability_payload": false, + "log_debug_namespace_ignore": "", + "log_debug_to_mqtt_frontend": false, + "log_directory": "/opt/zigbee2mqtt/data/log/%TIMESTAMP%", + "log_file": "log.log", + "log_level": "info", + "log_namespaced_levels": {}, + "log_output": [ + "console" + ], + "log_rotation": false, + "log_symlink_current": false, + "log_syslog": { + "app_name": "Zigbee2MQTT", + "eol": "/n", + "host": "localhost", + "localhost": "localhost", + "path": "/dev/log", + "pid": "process.pid", + "port": 514, + "protocol": "udp4", + "type": "5424" + }, + "output": "json", + "pan_id": 47288, + "report": false, + "soft_reset_timeout": 0, + "timestamp_format": "YYYY-MM-DD HH:mm:ss", + "transmit_power": 20 + }, + "automations": "automations.yaml", + "availability": { + "active": { + "timeout": 10 + }, + "passive": { + "timeout": 1500 + } + }, + "blocklist": [], + "device_options": { + "color_sync": true, + "legacy": false, + "state_action": false, + "transition": 0 + }, + "devices": { + "0x00124b0023b86d16": { + "friendly_name": "Motion sensor" + }, + "0x00158d0007e778c1": { + "friendly_name": "Occupancy sensor", + "no_occupancy_since": [], + "occupancy_timeout": 60 + }, + "0x00158d0008066798": { + "friendly_name": "Scenes button", + "legacy": false + }, + "0x00158d00091e7bd6": { + "friendly_name": "Contact sensor" + }, + "0x00158d000945f2b1": { + "friendly_name": "Climate sensor" + }, + "0x00158d00094d5a84": { + "friendly_name": "Leak sensor" + }, + "0x00158d0009e19292": { + "friendly_name": "Vibration sensor" + }, + "0x0015bc0036001b9e": { + "friendly_name": "Develco air quality" + }, + "0x04cd15fffe0d0ebd": { + "friendly_name": "Inrr RGBCTT light" + }, + "0x04cd15fffeddccd4": { + "friendly_name": "Gledopto GL-SD-001" + }, + "0x187a3efffe3560cb": { + "friendly_name": "Moes Dimmer" + }, + "0x385b44fffe20cfb2": { + "friendly_name": "Smart button" + }, + "0x54ef4410004695ae": { + "friendly_name": "Aqara switch no neutral" + }, + "0x54ef44100072a2ea": { + "friendly_name": "Occupancy sensor P1", + "occupancy_timeout": 60 + }, + "0x54ef44100085c321": { + "friendly_name": "Light sensor" + }, + "0x54ef44100086d51f": { + "energy_precision": 3, + "friendly_name": "Aqara switch T1" + }, + "0x54ef4410009716a8": { + "energy_precision": 3, + "friendly_name": "Aqara Dual relay module T2", + "state_action": false + }, + "0x60a423fffeae75ab": { + "friendly_name": "Moes switch", + "state_action": false + }, + "0x70ac08fffee6dd5e": { + "friendly_name": "Window shutter", + "invert_cover": false + }, + "0x847127fffeaff50a": { + "friendly_name": "Moes dimmer double" + }, + "0x84fd27fffe609c1b": { + "friendly_name": "Moes Dimmer double" + }, + "0xa4c138071003495f": { + "color_sync": false, + "friendly_name": "Gledopto RGBCTT light I", + "state_action": false + }, + "0xa4c1381710868c6b": { + "debounce": 1, + "friendly_name": "Smart air detector (_TZE200_c2fmom5z)" + }, + "0xa4c1381b458a63b3": { + "debounce": 1, + "friendly_name": "Smart air box 2 (_TZE200_mja3fuja)" + }, + "0xa4c138363dff5c1d": { + "friendly_name": "Eglo RGBCTT light" + }, + "0xa4c138479d307a4b": { + "debounce": 2, + "friendly_name": "Tuya Air Quality Sensor (_TZE200_yvx5lh6k)", + "homeassistant": {}, + "optimistic": true + }, + "0xa4c1385c96f83f3f": { + "debounce": 1, + "friendly_name": "Air house keeper (_TZE200_dwcarsat)" + }, + "0xa4c138636c5c666e": { + "friendly_name": "Carbon Monoxide sensor" + }, + "0xa4c13876c0fe9c0d": { + "color_sync": false, + "friendly_name": "Gledopto RGBCTT light II", + "state_action": false + }, + "0xa4c1388ad0ebb0a6": { + "color_sync": true, + "friendly_name": "Moes RGBCCT light bulb", + "state_action": false + }, + "0xa4c13894df46cbee": { + "friendly_name": "Plug" + }, + "0xa4c138a2a00bb8d4": { + "color_sync": true, + "friendly_name": "Moes RGBCCT light bulb II", + "state_action": false + }, + "0xa4c138c209c7b4de": { + "friendly_name": "Presence sensor" + }, + "0xa4c138c9bb78fd9c": { + "friendly_name": "Plug II" + }, + "0xa4c138dbda3532cb": { + "friendly_name": "Mini luminance motion sensor" + }, + "0xa4c138e0a08604f5": { + "color_sync": true, + "friendly_name": "Moes RGBCCT light bulb III", + "state_action": false + }, + "0xa4c138f402dfba8a": { + "debounce": 2, + "friendly_name": "Moes thermo" + }, + "0xcc86ecfffe4e9d25": { + "friendly_name": "Moes switch double", + "state_action": false + }, + "0xdc8e95fffe3f2eed": { + "energy_precision": 3, + "friendly_name": "EARU Circuit Breaker 16A", + "state_action": false + }, + "0xdc8e95fffe3f4853": { + "energy_precision": 3, + "friendly_name": "EARU Circuit Breaker 32A", + "state_action": false + } + }, + "external_converters": [], + "frontend": { + "base_url": "/", + "port": 8080 + }, + "groups": { + "1": { + "friendly_name": "At home" + }, + "2": { + "friendly_name": "Is dark" + }, + "3": { + "friendly_name": "Is night" + }, + "4": { + "friendly_name": "Sleeping" + }, + "5": { + "friendly_name": "Guests" + }, + "6": { + "friendly_name": "Master motion" + }, + "7": { + "devices": [ + "0xa4c138363dff5c1d/1", + "0xa4c138071003495f/11", + "0x04cd15fffe0d0ebd/1", + "0xa4c1388ad0ebb0a6/1", + "0xa4c138a2a00bb8d4/1", + "0xa4c138e0a08604f5/1" + ], + "friendly_name": "Lights" + }, + "8": { + "devices": [ + "0x54ef44100086d51f/1", + "0x54ef4410009716a8/1", + "0x54ef4410009716a8/2", + "0xcc86ecfffe4e9d25/2", + "0xcc86ecfffe4e9d25/1" + ], + "friendly_name": "Switches" + }, + "9": { + "devices": [ + "0x70ac08fffee6dd5e/1" + ], + "friendly_name": "Covers" + }, + "10": { + "devices": [ + "0xa4c138f402dfba8a/1" + ], + "friendly_name": "Thermostats" + } + }, + "homeassistant": false, + "map_options": { + "graphviz": { + "colors": { + "fill": { + "coordinator": "#e04e5d", + "enddevice": "#fff8ce", + "router": "#4ea3e0" + }, + "font": { + "coordinator": "#ffffff", + "enddevice": "#000000", + "router": "#ffffff" + }, + "line": { + "active": "#009900", + "inactive": "#994444" + } + } + } + }, + "matterbridge": { + "black_list": [], + "debug_level": 1, + "enabled": false, + "install_on_start": false, + "reset_on_start": false, + "unregister_on_stop": false, + "white_list": [] + }, + "mqtt": { + "base_topic": "zigbee2mqtt", + "force_disable_retain": false, + "include_device_information": false, + "keepalive": 60, + "reject_unauthorized": false, + "server": "mqtt://localhost:1883", + "user": "lligu", + "version": 5 + }, + "ota": { + "disable_automatic_update_check": false, + "update_check_interval": 1440 + }, + "passlist": [], + "permit_join": false, + "serial": { + "adapter": "zstack", + "baudrate": 115200, + "disable_led": false, + "ex_port": "\\\\.\\COM3", + "port": "tcp://192.168.1.73:6638", + "rtscts": false + } + }, + "config_schema": { + "definitions": { + "device": { + "properties": { + "debounce": { + "description": "Debounces messages of this device", + "title": "Debounce", + "type": "number" + }, + "debounce_ignore": { + "description": "Protects unique payload values of specified payload properties from overriding within debounce time", + "examples": [ + "action" + ], + "items": { + "type": "string" + }, + "title": "Ignore debounce", + "type": "array" + }, + "disabled": { + "description": "Disables the device (excludes device from network scans, availability and group state updates)", + "requiresRestart": true, + "title": "Disabled", + "type": "boolean" + }, + "filtered_attributes": { + "description": "Filter attributes with regex from published payload.", + "examples": [ + "^temperature$", + "^battery$", + "^action$" + ], + "items": { + "type": "string" + }, + "title": "Filtered publish attributes", + "type": "array" + }, + "filtered_cache": { + "description": "Filter attributes with regex from being added to the cache, this prevents the attribute from being in the published payload when the value didn't change.", + "examples": [ + "^input_actions$" + ], + "items": { + "type": "string" + }, + "title": "Filtered attributes from cache", + "type": "array" + }, + "filtered_optimistic": { + "description": "Filter attributes with regex from optimistic publish payload when calling /set. (This has no effect if optimistic is set to false).", + "examples": [ + "^color_(mode|temp)$", + "color" + ], + "items": { + "type": "string" + }, + "title": "Filtered optimistic attributes", + "type": "array" + }, + "friendly_name": { + "description": "Used in the MQTT topic of a device. By default this is the device ID", + "readOnly": true, + "title": "Friendly name", + "type": "string" + }, + "homeassistant": { + "properties": { + "name": { + "description": "Name of the device in Home Assistant", + "title": "Home Assistant name", + "type": "string" + } + }, + "title": "Home Assistant", + "type": [ + "object", + "null" + ] + }, + "icon": { + "description": "The user-defined device icon for the frontend. It can be a full URL link to an image (e.g. https://SOME.SITE/MODEL123.jpg) (you cannot use a path to a local file) or base64 encoded data URL (e.g. image/svg+xml;base64,PHN2ZyB3aW....R0aD)", + "title": "Icon", + "type": "string" + }, + "optimistic": { + "default": true, + "description": "Publish optimistic state after set", + "title": "Optimistic", + "type": "boolean" + }, + "qos": { + "description": "QoS level for MQTT messages of this device", + "title": "QoS", + "type": "number" + }, + "retain": { + "description": "Retain MQTT messages of this device", + "title": "Retain", + "type": "boolean" + }, + "retention": { + "description": "Sets the MQTT Message Expiry in seconds, Make sure to set mqtt.version to 5", + "title": "Retention", + "type": "number" + } + }, + "required": [ + "friendly_name" + ], + "type": "object" + }, + "group": { + "properties": { + "devices": { + "items": { + "type": "string" + }, + "type": "array" + }, + "filtered_attributes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "friendly_name": { + "type": "string" + }, + "off_state": { + "default": "auto", + "description": "Control when to publish state OFF or CLOSE for a group. 'all_members_off': only publish state OFF/CLOSE when all group members are in state OFF/CLOSE, 'last_member_state': publish state OFF whenever one of its members changes to OFF", + "enum": [ + "all_members_off", + "last_member_state" + ], + "requiresRestart": true, + "title": "Group off state", + "type": [ + "string" + ] + }, + "optimistic": { + "type": "boolean" + }, + "qos": { + "type": "number" + }, + "retain": { + "type": "boolean" + } + }, + "required": [ + "friendly_name" + ], + "type": "object" + } + }, + "properties": { + "advanced": { + "properties": { + "adapter_concurrent": { + "description": "Adapter concurrency (e.g. 2 for CC2531 or 16 for CC26X2R1) (default: null, uses recommended value)", + "maximum": 64, + "minimum": 1, + "requiresRestart": true, + "title": "Adapter concurrency", + "type": [ + "number", + "null" + ] + }, + "adapter_delay": { + "description": "Adapter delay", + "maximum": 1000, + "minimum": 0, + "requiresRestart": true, + "title": "Adapter delay", + "type": [ + "number", + "null" + ] + }, + "cache_state": { + "default": true, + "description": "MQTT message payload will contain all attributes, not only changed ones. Has to be true when integrating via Home Assistant", + "title": "Cache state", + "type": "boolean" + }, + "cache_state_persistent": { + "default": true, + "description": "Persist cached state, only used when cache_state: true", + "title": "Persist cache state", + "type": "boolean" + }, + "cache_state_send_on_startup": { + "default": true, + "description": "Send cached state on startup, only used when cache_state: true", + "title": "Send cached state on startup", + "type": "boolean" + }, + "channel": { + "default": 11, + "description": "Zigbee channel, changing might require re-pairing some devices! (Note: use a ZLL channel: 11, 15, 20, or 25 to avoid problems)", + "examples": [ + 15, + 20, + 25 + ], + "maximum": 26, + "minimum": 11, + "requiresRestart": true, + "title": "ZigBee channel", + "type": "number" + }, + "elapsed": { + "default": false, + "description": "Add an elapsed attribute to MQTT messages, contains milliseconds since the previous msg", + "title": "Elapsed", + "type": "boolean" + }, + "ext_pan_id": { + "description": "Zigbee extended pan ID, changing requires re-pairing all devices!", + "oneOf": [ + { + "title": "Extended pan ID (string)", + "type": "string" + }, + { + "items": { + "type": "number" + }, + "title": "Extended pan ID (array)", + "type": "array" + } + ], + "requiresRestart": true, + "title": "Ext Pan ID" + }, + "last_seen": { + "default": "disable", + "description": "Add a last_seen attribute to MQTT messages, contains date/time of last Zigbee message", + "enum": [ + "disable", + "ISO_8601", + "ISO_8601_local", + "epoch" + ], + "title": "Last seen", + "type": "string" + }, + "legacy_api": { + "default": true, + "description": "Disables the legacy api (false = disable)", + "requiresRestart": true, + "title": "Legacy API", + "type": "boolean" + }, + "legacy_availability_payload": { + "default": true, + "description": "Payload to be used for device availability and bridge/state topics. true = text, false = JSON", + "requiresRestart": true, + "title": "Legacy availability payload", + "type": "boolean" + }, + "log_debug_namespace_ignore": { + "default": "", + "description": "Do not log these namespaces (regex-based) for debug level", + "examples": [ + "^zhc:legacy:fz:(tuya|moes)", + "^zhc:legacy:fz:(tuya|moes)|^zh:ember:uart:|^zh:controller" + ], + "title": "Log debug namespace ignore", + "type": "string" + }, + "log_debug_to_mqtt_frontend": { + "default": false, + "description": "Log debug level to MQTT and frontend (may decrease overall performance)", + "requiresRestart": true, + "title": "Log debug to MQTT and frontend", + "type": "boolean" + }, + "log_directory": { + "description": "Location of log directory", + "examples": [ + "data/log/%TIMESTAMP%" + ], + "requiresRestart": true, + "title": "Log directory", + "type": "string" + }, + "log_file": { + "default": "log.txt", + "description": "Log file name, can also contain timestamp", + "examples": [ + "zigbee2mqtt_%TIMESTAMP%.log" + ], + "requiresRestart": true, + "title": "Log file", + "type": "string" + }, + "log_level": { + "default": "info", + "description": "Logging level", + "enum": [ + "error", + "warning", + "info", + "debug", + "warn" + ], + "title": "Log level", + "type": "string" + }, + "log_namespaced_levels": { + "additionalProperties": { + "enum": [ + "error", + "warning", + "info", + "debug" + ], + "type": "string" + }, + "default": {}, + "description": "Set individual log levels for certain namespaces", + "examples": [ + { + "z2m:mqtt": "warning" + }, + { + "zh:ember:uart:ash": "info" + } + ], + "propertyNames": { + "pattern": "^(z2m|zhc|zh)(:[a-z0-9]{1,})*$" + }, + "title": "Log Namespaced Levels", + "type": "object" + }, + "log_output": { + "description": "Output location of the log, leave empty to suppress logging", + "items": { + "enum": [ + "console", + "file", + "syslog" + ], + "type": "string" + }, + "requiresRestart": true, + "title": "Log output", + "type": "array" + }, + "log_rotation": { + "default": true, + "description": "Log rotation", + "requiresRestart": true, + "title": "Log rotation", + "type": "boolean" + }, + "log_symlink_current": { + "default": false, + "description": "Create symlink to current logs in the log directory", + "requiresRestart": true, + "title": "Log symlink current", + "type": "boolean" + }, + "log_syslog": { + "oneOf": [ + { + "title": "syslog (disabled)", + "type": "null" + }, + { + "properties": { + "app_name": { + "default": "Zigbee2MQTT", + "description": "The name of the application (Default: Zigbee2MQTT).", + "title": "Localhost", + "type": "string" + }, + "eol": { + "default": "/n", + "description": "The end of line character to be added to the end of the message (Default: Message without modifications).", + "title": "eol", + "type": "string" + }, + "host": { + "default": "localhost", + "description": "The host running syslogd, defaults to localhost.", + "title": "Host", + "type": "string" + }, + "localhost": { + "default": "localhost", + "description": "Host to indicate that log messages are coming from (Default: localhost).", + "title": "Localhost", + "type": "string" + }, + "path": { + "default": "/dev/log", + "description": "The path to the syslog dgram socket (i.e. /dev/log or /var/run/syslog for OS X).", + "examples": [ + "/var/run/syslog" + ], + "title": "Path", + "type": "string" + }, + "pid": { + "default": "process.pid", + "description": "PID of the process that log messages are coming from (Default process.pid).", + "title": "PID", + "type": "string" + }, + "port": { + "default": 514, + "description": "The port on the host that syslog is running on, defaults to syslogd's default port.", + "title": "Port", + "type": "number" + }, + "protocol": { + "default": "udp4", + "description": "The network protocol to log over (e.g. tcp4, udp4, tls4, unix, unix-connect, etc).", + "examples": [ + "udp4", + "tls4", + "unix", + "unix-connect" + ], + "title": "Protocol", + "type": "string" + }, + "type": { + "default": "5424", + "description": "The type of the syslog protocol to use (Default: BSD, also valid: 5424).", + "title": "Type", + "type": "string" + } + }, + "title": "syslog (enabled)", + "type": "object" + } + ], + "requiresRestart": true + }, + "network_key": { + "description": "Network encryption key, changing requires re-pairing all devices!", + "oneOf": [ + { + "title": "Network key(string)", + "type": "string" + }, + { + "items": { + "type": "number" + }, + "title": "Network key(array)", + "type": "array" + } + ], + "requiresRestart": true, + "title": "Network key" + }, + "output": { + "description": "Examples when 'state' of a device is published json: topic: 'zigbee2mqtt/my_bulb' payload '{\"state\": \"ON\"}' attribute: topic 'zigbee2mqtt/my_bulb/state' payload 'ON' attribute_and_json: both json and attribute (see above)", + "enum": [ + "attribute_and_json", + "attribute", + "json" + ], + "title": "MQTT output type", + "type": "string" + }, + "pan_id": { + "description": "ZigBee pan ID, changing requires re-pairing all devices!", + "oneOf": [ + { + "title": "Pan ID (string)", + "type": "string" + }, + { + "title": "Pan ID (number)", + "type": "number" + } + ], + "requiresRestart": true, + "title": "Pan ID" + }, + "timestamp_format": { + "description": "Log timestamp format", + "examples": [ + "YYYY-MM-DD HH:mm:ss" + ], + "requiresRestart": true, + "title": "Timestamp format", + "type": "string" + }, + "transmit_power": { + "description": "Transmit power of adapter, only available for Z-Stack (CC253*/CC2652/CC1352) adapters, CC2652 = 5dbm, CC1352 max is = 20dbm (5dbm default)", + "maximum": 127, + "minimum": -128, + "requiresRestart": true, + "title": "Transmit power", + "type": [ + "number", + "null" + ] + } + }, + "title": "Advanced", + "type": "object" + }, + "availability": { + "description": "Checks whether devices are online/offline", + "oneOf": [ + { + "title": "Availability (simple)", + "type": "boolean" + }, + { + "properties": { + "active": { + "description": "Options for active devices (routers/mains powered)", + "properties": { + "timeout": { + "default": 10, + "description": "Time after which an active device will be marked as offline in minutes", + "requiresRestart": true, + "title": "Timeout", + "type": "number" + } + }, + "requiresRestart": true, + "title": "Active", + "type": "object" + }, + "passive": { + "description": "Options for passive devices (mostly battery powered)", + "properties": { + "timeout": { + "default": 1500, + "description": "Time after which an passive device will be marked as offline in minutes", + "requiresRestart": true, + "title": "Timeout", + "type": "number" + } + }, + "requiresRestart": true, + "title": "Passive", + "type": "object" + } + }, + "title": "Availability (advanced)", + "type": "object" + } + ], + "requiresRestart": true, + "title": "Availability" + }, + "ban": { + "items": { + "type": "string" + }, + "readOnly": true, + "requiresRestart": true, + "title": "Ban (deprecated, use blocklist)", + "type": "array" + }, + "blocklist": { + "description": "Block devices from the network (by ieeeAddr)", + "items": { + "type": "string" + }, + "requiresRestart": true, + "title": "Blocklist", + "type": "array" + }, + "device_options": { + "title": "Options that are applied to all devices", + "type": "object" + }, + "devices": { + "patternProperties": { + "^.*$": { + "$ref": "#/definitions/device" + } + }, + "propertyNames": { + "pattern": "^0x[\\d\\w]{16}$" + }, + "type": "object" + }, + "external_converters": { + "description": "You can define external converters to e.g. add support for a DiY device", + "examples": [ + "DIYRuZ_FreePad.js" + ], + "items": { + "type": "string" + }, + "requiresRestart": true, + "title": "External converters", + "type": "array" + }, + "frontend": { + "oneOf": [ + { + "title": "Frontend (simple)", + "type": "boolean" + }, + { + "properties": { + "auth_token": { + "description": "Enables authentication, disabled by default", + "requiresRestart": true, + "title": "Auth token", + "type": [ + "string", + "null" + ] + }, + "base_url": { + "default": "/", + "description": "Base URL for the frontend. If hosted under a subpath, e.g. 'http://localhost:8080/z2m', set this to '/z2m'", + "pattern": "^\\/.*", + "requiresRestart": true, + "title": "Base URL", + "type": "string" + }, + "host": { + "description": "Frontend binding host. Binds to a unix socket when an absolute path is given instead.", + "examples": [ + "127.0.0.1", + "::1", + "/run/zigbee2mqtt/zigbee2mqtt.sock" + ], + "requiresRestart": true, + "title": "Bind host", + "type": [ + "string", + "null" + ] + }, + "port": { + "default": 8080, + "description": "Frontend binding port. Ignored when using a unix domain socket", + "requiresRestart": true, + "title": "Port", + "type": "number" + }, + "ssl_cert": { + "description": "SSL Certificate file path for exposing HTTPS. The sibling property 'ssl_key' must be set for HTTPS to be activated.", + "requiresRestart": true, + "title": "Certificate file path", + "type": [ + "string", + "null" + ] + }, + "ssl_key": { + "description": "SSL key file path for exposing HTTPS. The sibling property 'ssl_cert' must be set for HTTPS to be activated.", + "requiresRestart": true, + "title": "key file path", + "type": [ + "string", + "null" + ] + }, + "url": { + "description": "URL on which the frontend can be reached, currently only used for the Home Assistant device configuration page", + "requiresRestart": true, + "title": "URL", + "type": [ + "string", + "null" + ] + } + }, + "title": "Frontend (advanced)", + "type": "object" + } + ], + "requiresRestart": true, + "title": "Frontend" + }, + "groups": { + "patternProperties": { + "^.*$": { + "$ref": "#/definitions/group" + } + }, + "propertyNames": { + "pattern": "^[\\w].*$" + }, + "type": "object" + }, + "homeassistant": { + "default": false, + "description": "Home Assistant integration (MQTT discovery)", + "oneOf": [ + { + "title": "Home Assistant (simple)", + "type": "boolean" + }, + { + "properties": { + "discovery_topic": { + "description": "Home Assistant discovery topic", + "examples": [ + "homeassistant" + ], + "requiresRestart": true, + "title": "Homeassistant discovery topic", + "type": "string" + }, + "legacy_entity_attributes": { + "default": true, + "description": "Home Assistant legacy entity attributes, when enabled Zigbee2MQTT will add state attributes to each entity, additional to the separate entities and devices it already creates", + "title": "Home Assistant legacy entity attributes", + "type": "boolean" + }, + "legacy_triggers": { + "default": true, + "description": "Home Assistant legacy triggers, when enabled Zigbee2mqt will send an empty 'action' or 'click' after one has been send. A 'sensor_action' and 'sensor_click' will be discoverd", + "title": "Home Assistant legacy triggers", + "type": "boolean" + }, + "status_topic": { + "description": "Home Assistant status topic", + "examples": [ + "homeassistant/status" + ], + "requiresRestart": true, + "title": "Home Assistant status topic", + "type": "string" + } + }, + "title": "Home Assistant (advanced)", + "type": "object" + } + ], + "requiresRestart": true, + "title": "Home Assistant integration" + }, + "map_options": { + "properties": { + "graphviz": { + "properties": { + "colors": { + "properties": { + "fill": { + "properties": { + "coordinator": { + "type": "string" + }, + "enddevice": { + "type": "string" + }, + "router": { + "type": "string" + } + }, + "type": "object" + }, + "font": { + "properties": { + "coordinator": { + "type": "string" + }, + "enddevice": { + "type": "string" + }, + "router": { + "type": "string" + } + }, + "type": "object" + }, + "line": { + "properties": { + "active": { + "type": "string" + }, + "inactive": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "title": "Networkmap", + "type": "object" + }, + "mqtt": { + "properties": { + "base_topic": { + "default": "zigbee2mqtt", + "description": "MQTT base topic for Zigbee2MQTT MQTT messages", + "examples": [ + "zigbee2mqtt" + ], + "requiresRestart": true, + "title": "Base topic", + "type": "string" + }, + "ca": { + "description": "Absolute path to SSL/TLS certificate of CA used to sign server and client certificates", + "examples": [ + "/etc/ssl/mqtt-ca.crt" + ], + "requiresRestart": true, + "title": "Certificate authority", + "type": "string" + }, + "cert": { + "description": "Absolute path to SSL/TLS certificate for client-authentication", + "examples": [ + "/etc/ssl/mqtt-client.crt" + ], + "requiresRestart": true, + "title": "SSL/TLS certificate", + "type": "string" + }, + "client_id": { + "description": "MQTT client ID", + "examples": [ + "MY_CLIENT_ID" + ], + "requiresRestart": true, + "title": "Client ID", + "type": "string" + }, + "force_disable_retain": { + "default": false, + "description": "Disable retain for all send messages. ONLY enable if you MQTT broker doesn't support retained message (e.g. AWS IoT core, Azure IoT Hub, Google Cloud IoT core, IBM Watson IoT Platform). Enabling will break the Home Assistant integration", + "requiresRestart": true, + "title": "Force disable retain", + "type": "boolean" + }, + "include_device_information": { + "default": false, + "description": "Include device information to mqtt messages", + "title": "Include device information", + "type": "boolean" + }, + "keepalive": { + "default": 60, + "description": "MQTT keepalive in second", + "requiresRestart": true, + "title": "Keepalive", + "type": "number" + }, + "key": { + "description": "Absolute path to SSL/TLS key for client-authentication", + "examples": [ + "/etc/ssl/mqtt-client.key" + ], + "requiresRestart": true, + "title": "SSL/TLS key", + "type": "string" + }, + "password": { + "description": "MQTT server authentication password", + "examples": [ + "ILOVEPELMENI" + ], + "requiresRestart": true, + "title": "Password", + "type": "string" + }, + "reject_unauthorized": { + "default": true, + "description": "Disable self-signed SSL certificate", + "requiresRestart": true, + "title": "Reject unauthorized", + "type": "boolean" + }, + "server": { + "description": "MQTT server URL (use mqtts:// for SSL/TLS connection)", + "examples": [ + "mqtt://localhost:1883" + ], + "requiresRestart": true, + "title": "MQTT server", + "type": "string" + }, + "user": { + "description": "MQTT server authentication user", + "examples": [ + "johnnysilverhand" + ], + "requiresRestart": true, + "title": "User", + "type": "string" + }, + "version": { + "default": 4, + "description": "MQTT protocol version", + "examples": [ + 5 + ], + "requiresRestart": true, + "title": "Version", + "type": [ + "number", + "null" + ] + } + }, + "required": [ + "server" + ], + "title": "MQTT", + "type": "object" + }, + "ota": { + "properties": { + "disable_automatic_update_check": { + "default": false, + "description": "Zigbee devices may request a firmware update, and do so frequently, causing Zigbee2MQTT to reach out to third party servers. If you disable these device initiated checks, you can still initiate a firmware update check manually.", + "title": "Disable automatic update check", + "type": "boolean" + }, + "ikea_ota_use_test_url": { + "default": false, + "description": "Use IKEA TRADFRI OTA test server, see OTA updates documentation", + "requiresRestart": true, + "title": "IKEA TRADFRI OTA use test url", + "type": "boolean" + }, + "update_check_interval": { + "default": 1440, + "description": "Your device may request a check for a new firmware update. This value determines how frequently third party servers may actually be contacted to look for firmware updates. The value is set in minutes, and the default is 1 day.", + "title": "Update check interval", + "type": "number" + }, + "zigbee_ota_override_index_location": { + "description": "Location of override OTA index file", + "examples": [ + "index.json" + ], + "requiresRestart": true, + "title": "OTA index override file name", + "type": [ + "string", + "null" + ] + } + }, + "title": "OTA updates", + "type": "object" + }, + "passlist": { + "description": "Allow only certain devices to join the network (by ieeeAddr). Note that all devices not on the passlist will be removed from the network!", + "items": { + "type": "string" + }, + "requiresRestart": true, + "title": "Passlist", + "type": "array" + }, + "permit_join": { + "default": false, + "description": "Allow new devices to join (re-applied at restart)", + "title": "Permit join", + "type": "boolean" + }, + "serial": { + "properties": { + "adapter": { + "default": "auto", + "description": "Adapter type, not needed unless you are experiencing problems", + "enum": [ + "deconz", + "zstack", + "zigate", + "ezsp", + "auto", + "ember", + "zboss" + ], + "requiresRestart": true, + "title": "Adapter", + "type": [ + "string" + ] + }, + "baudrate": { + "description": "Baud rate speed for serial port, this can be anything firmware support but default is 115200 for Z-Stack and EZSP, 38400 for Deconz, however note that some EZSP firmware need 57600", + "examples": [ + 38400, + 57600, + 115200 + ], + "requiresRestart": true, + "title": "Baudrate", + "type": "number" + }, + "disable_led": { + "default": false, + "description": "Disable LED of the adapter if supported", + "requiresRestart": true, + "title": "Disable led", + "type": "boolean" + }, + "port": { + "description": "Location of the adapter. To autodetect the port, set null", + "examples": [ + "/dev/ttyACM0" + ], + "requiresRestart": true, + "title": "Port", + "type": [ + "string", + "null" + ] + }, + "rtscts": { + "description": "RTS / CTS Hardware Flow Control for serial port", + "requiresRestart": true, + "title": "RTS / CTS", + "type": "boolean" + } + }, + "title": "Serial", + "type": "object" + }, + "whitelist": { + "items": { + "type": "string" + }, + "readOnly": true, + "requiresRestart": true, + "title": "Whitelist (deprecated, use passlist)", + "type": "array" + } + }, + "required": [ + "mqtt" + ], + "type": "object" + }, + "coordinator": { + "ieee_address": "0x00124b0025e1f196", + "meta": { + "maintrel": 1, + "majorrel": 2, + "minorrel": 7, + "product": 1, + "revision": 20240716, + "transportrev": 2 + }, + "type": "zStack3x0" + }, + "log_level": "info", + "network": { + "channel": 20, + "extended_pan_id": "0x3dc82d9aa32a2c92", + "pan_id": 47288 + }, + "permit_join": false, + "restart_required": false, + "version": "1.41.0", + "zigbee_herdsman": { + "version": "2.1.7" + }, + "zigbee_herdsman_converters": { + "version": "20.38.0" + } +} \ No newline at end of file From 38b0bdd18aecae5b5b0244e50b70fd3398c7531d Mon Sep 17 00:00:00 2001 From: Luligu Date: Sun, 24 Nov 2024 10:42:42 +0100 Subject: [PATCH 13/17] Dev 2.2.2-dev.6 --- package.json | 2 +- src/entity.ts | 14 ++++++++++---- src/platform.test.ts | 37 ++++++++++++++++++++++++++++++++++++- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index e8e4e2f..94da758 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matterbridge-zigbee2mqtt", - "version": "2.2.2-dev.5", + "version": "2.2.2-dev.6", "description": "Matterbridge zigbee2mqtt plugin", "author": "https://github.com/Luligu", "license": "Apache-2.0", diff --git a/src/entity.ts b/src/entity.ts index 5cd07bc..490b172 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -154,7 +154,7 @@ export class ZigbeeEntity extends EventEmitter { } } - // Use with this.addTagList(endpoint, null, NumberTag.One.namespaceId, NumberTag.One.tag, 'Label'); + // Use with this.addTagList(endpoint, null, NumberTag.One.namespaceId, NumberTag.One.tag); // this.addTagList(endpoint, null, SwitchesTag.Custom.namespaceId, SwitchesTag.Custom.tag, 'Label'); addTagList(endpoint: Endpoint, mfgCode: VendorId | null, namespaceId: number, tag: number, label?: string | null) { const descriptor = endpoint.getClusterServer(DescriptorCluster.with(Descriptor.Feature.TagList)); @@ -165,6 +165,7 @@ export class ZigbeeEntity extends EventEmitter { // tagList: { mfgCode: VendorId | null; namespaceId: number; tag: number; label?: string | null }[] = []; + // We have already the feature, just add the new tag if (descriptor.attributes.tagList) { this.log.debug(`addTagList: adding ${CYAN}tagList${db} mfCode: ${mfgCode}, namespaceId: ${namespaceId}, tag: ${tag}, label: ${label} on endpoint $${endpoint.name}:${endpoint.number}`); const tagList = descriptor.attributes.tagList.getLocal(); @@ -172,6 +173,7 @@ export class ZigbeeEntity extends EventEmitter { return; } + // We don't have the feature, we recreate descriptor with the tag this.log.debug(`addTagList: creating ${CYAN}tagList${db} mfCode: ${mfgCode}, namespaceId: ${namespaceId}, tag: ${tag}, label: ${label} on endpoint $${endpoint.name}:${endpoint.number}`); endpoint.addClusterServer( ClusterServer( @@ -213,7 +215,7 @@ export class ZigbeeEntity extends EventEmitter { // Check if the message is a duplicate that can be ingored cause only linkquality and last_seen have changed (action is always passed) const now = Date.now(); if (now - this.lastSeen < 1000 * 60 && deepEqual(this.lastPayload, payload, ['linkquality', 'last_seen', ...this.ignoreFeatures]) && !Object.prototype.hasOwnProperty.call(this.lastPayload, 'action')) { - this.log.debug(`*Skipping not changed MQTT message for device ${this.ien}${this.entityName}${rs}${db} payload: ${debugStringify(payload)}`); + this.log.debug(`Skipping not changed ${platform.z2mDevicesRegistered ? 'MQTT message' : 'State update'} for accessory ${this.entityName}`); return; } this.lastSeen = Date.now(); @@ -228,15 +230,16 @@ export class ZigbeeEntity extends EventEmitter { if (Object.prototype.hasOwnProperty.call(payload, key)) { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete payload[key]; + this.log.debug(`Removed key ${CYAN}${key}${db} from payload`); } } if (this.bridgedDevice === undefined) { - this.log.debug(`*Skipping (no device) ${platform.z2mDevicesRegistered ? 'MQTT message' : 'State update'} for accessory ${this.ien}${this.entityName}${rs}${db} payload: ${debugStringify(payload)}`); + this.log.debug(`Skipping (no device) ${platform.z2mDevicesRegistered ? 'MQTT message' : 'State update'} for accessory ${this.entityName}`); return; } if (this.noUpdate) { - this.log.debug(`*Skipping (no update) ${platform.z2mDevicesRegistered ? 'MQTT message' : 'State update'} for accessory ${this.ien}${this.entityName}${rs}${db} payload: ${debugStringify(payload)}`); + this.log.debug(`Skipping (no update) ${platform.z2mDevicesRegistered ? 'MQTT message' : 'State update'} for accessory ${this.entityName}`); return; } this.log.info(`${db}${platform.z2mDevicesRegistered ? 'MQTT message' : 'State update'} for device ${this.ien}${this.entityName}${rs}${db} payload: ${debugStringify(payload)}`); @@ -324,6 +327,9 @@ export class ZigbeeEntity extends EventEmitter { if (key === 'color' && 'color_mode' in payload && payload['color_mode'] === 'xy') { const { x, y } = value as { x: number; y: number }; const hsl = color.xyToHsl(x, y); + const rgb = color.xyColorToRgbColor(x, y); + this.log.debug(`ColorControl xyToHsl ${CYAN}${x}${db} ${CYAN}${y}${db} => h ${CYAN}${hsl.h}${db} s ${CYAN}${hsl.s}${db} l ${CYAN}${hsl.l}${db}`); + this.log.debug(`ColorControl xyToRgb ${CYAN}${x}${db} ${CYAN}${y}${db} => r ${CYAN}${rgb.r}${db} g ${CYAN}${rgb.g}${db} b ${CYAN}${rgb.b}${db}`); this.updateAttributeIfChanged(this.bridgedDevice, undefined, ColorControl.Cluster.id, 'currentHue', Math.round((hsl.h / 360) * 254)); this.updateAttributeIfChanged(this.bridgedDevice, undefined, ColorControl.Cluster.id, 'currentSaturation', Math.round((hsl.s / 100) * 254)); this.updateAttributeIfChanged(this.bridgedDevice, undefined, ColorControl.Cluster.id, 'colorMode', ColorControl.ColorMode.CurrentHueAndCurrentSaturation); diff --git a/src/platform.test.ts b/src/platform.test.ts index bfe578d..4c72ab0 100644 --- a/src/platform.test.ts +++ b/src/platform.test.ts @@ -5,7 +5,7 @@ import { jest } from '@jest/globals'; import { Matterbridge, PlatformConfig } from 'matterbridge'; -import { AnsiLogger, idn, ign, LogLevel, rs, TimestampFormat, wr } from 'matterbridge/logger'; +import { AnsiLogger, db, idn, ign, LogLevel, rs, TimestampFormat, wr, debugStringify, or, hk, zb } from 'matterbridge/logger'; import { wait } from 'matterbridge/utils'; import { ZigbeePlatform } from './platform'; @@ -261,6 +261,41 @@ describe('TestPlatform', () => { await wait(200); }); + it('should update /At home/set', async () => { + const entity = 'At home'; + const payload = { state: 'ON', changed: 1 }; + (z2mPlatform.z2m as any).messageHandler('zigbee2mqtt/' + entity, Buffer.from(JSON.stringify(payload))); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringContaining(`${db}MQTT message for device ${ign}${entity}${rs}${db} payload:`)); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringContaining(`${db}Update endpoint ${or}MA-onoffswitch:undefined${db} attribute ${hk}OnOff${db}.${hk}onOff${db} from ${zb}false${db} to ${zb}true${db}`)); + + await wait(200); + }); + + it('should update /Lights/set', async () => { + // loggerLogSpy.mockRestore(); + // consoleLogSpy.mockRestore(); + + // {"entity":"Lights","payload":"{\"brightness\":254,\"color\":{\"hue\":22,\"saturation\":97,\"x\":0.5492,\"y\":0.4082},\"color_mode\":\"color_temp\",\"color_temp\":555,\"state\":\"OFF\"}"} + const entity = 'Lights'; + + const oldxy = { state: 'OFF', brightness: 100, color: { x: 0.2927, y: 0.6349 }, color_mode: 'xy', changed: 0 }; // { name: 'Pure Green 50% 120', hsl: { h: 120, s: 50, l: 50 }, rgb: { r: 64, g: 192, b: 64 }, xy: { x: 0.2127, y: 0.6349 } }, + (z2mPlatform.z2m as any).messageHandler('zigbee2mqtt/' + entity, Buffer.from(JSON.stringify(oldxy))); + + const oldct = { state: 'OFF', brightness: 100, color_temp: 500, color_mode: 'color_temp', changed: 0 }; + (z2mPlatform.z2m as any).messageHandler('zigbee2mqtt/' + entity, Buffer.from(JSON.stringify(oldct))); + + const payload = { state: 'ON', brightness: 250, color: { x: 0.7006, y: 0.2993 }, color_mode: 'xy', changed: 1 }; // { name: 'Pure Red 0', hsl: { h: 0, s: 100, l: 50 }, rgb: { r: 255, g: 0, b: 0 }, xy: { x: 0.7006, y: 0.2993 } }, + (z2mPlatform.z2m as any).messageHandler('zigbee2mqtt/' + entity, Buffer.from(JSON.stringify(payload))); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringContaining(`${db}MQTT message for device ${ign}${entity}${rs}${db} payload:`)); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringContaining(`${db}Update endpoint ${or}MA-colortemperaturelight:undefined${db} attribute ${hk}OnOff${db}.${hk}onOff${db} from ${zb}false${db} to ${zb}true${db}`)); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringContaining(`${db}Update endpoint ${or}MA-colortemperaturelight:undefined${db} attribute ${hk}LevelControl${db}.${hk}currentLevel${db} from ${zb}100${db} to ${zb}250${db}`)); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringContaining(`${db}Update endpoint ${or}MA-colortemperaturelight:undefined${db} attribute ${hk}ColorControl${db}.${hk}colorMode${db} from ${zb}2${db} to ${zb}0${db}`)); + // expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringContaining(`${db}Update endpoint ${or}MA-colortemperaturelight:undefined${db} attribute ${hk}ColorControl${db}.${hk}currentHue${db} from ${zb}85${db} to ${zb}0${db}`)); + // expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringContaining(`${db}Update endpoint ${or}MA-colortemperaturelight:undefined${db} attribute ${hk}ColorControl${db}.${hk}currentSaturation${db} from ${zb}100${db} to ${zb}100${db}`)); + + await wait(200); + }); + it('should call onConfigure', async () => { await z2mPlatform.onConfigure(); expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringMatching(/^Configured zigbee2mqtt dynamic platform/)); From 81bcb66dc2429ff927f76fc0179d4991863f97cd Mon Sep 17 00:00:00 2001 From: Luligu Date: Sun, 24 Nov 2024 10:45:14 +0100 Subject: [PATCH 14/17] Dev 2.2.2-dev.6 --- package-lock.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index fac8437..423cbce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "matterbridge-zigbee2mqtt", - "version": "2.2.2-dev.5", + "version": "2.2.2-dev.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "matterbridge-zigbee2mqtt", - "version": "2.2.2-dev.5", + "version": "2.2.2-dev.6", "license": "Apache-2.0", "dependencies": { "moment": "2.30.1", @@ -2131,9 +2131,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001683", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001683.tgz", - "integrity": "sha512-iqmNnThZ0n70mNwvxpEC2nBJ037ZHZUoBI5Gorh1Mw6IlEAZujEoU1tXA628iZfzm7R9FvFzxbfdgml82a3k8Q==", + "version": "1.0.30001684", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz", + "integrity": "sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==", "dev": true, "funding": [ { From fe966318418932c789c675f55b0dab9c5ed86b19 Mon Sep 17 00:00:00 2001 From: Luligu Date: Mon, 25 Nov 2024 16:23:51 +0100 Subject: [PATCH 15/17] Release 2.2.2 --- CHANGELOG.md | 7 +- package-lock.json | 10 +-- package.json | 2 +- src/entity.ts | 195 ++++++++++++++++++++++++++++------------------ src/platform.ts | 4 +- 5 files changed, 133 insertions(+), 85 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c94206..4afe9e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,9 @@ All notable changes to this project will be documented in this file. ### Added -- [thermostat]: Added min_temperature_limit and max_temperature_limit converter. -- [thermostat]: Added min_heat_setpoint_limit and max_heat_setpoint_limit converter. +- [thermostat]: Added min_temperature_limit and max_temperature_limit converter to thermostats. +- [thermostat]: Added min_heat_setpoint_limit and max_heat_setpoint_limit converter to thermostats. +- [thermostat]: Added configuration for heat only and cool only thermostats. - [matter]: Added tagList to child endpoints. ### Changed @@ -18,7 +19,7 @@ All notable changes to this project will be documented in this file. - [readme]: Updated install script. - [readme]: Updated build script. - [package]: Updated dependencies. -- [plugin]: Requires Matterbridge 1.6.1. +- [plugin]: Requires Matterbridge 1.6.2. ### Fixed diff --git a/package-lock.json b/package-lock.json index 423cbce..82c11c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "matterbridge-zigbee2mqtt", - "version": "2.2.2-dev.6", + "version": "2.2.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "matterbridge-zigbee2mqtt", - "version": "2.2.2-dev.6", + "version": "2.2.2", "license": "Apache-2.0", "dependencies": { "moment": "2.30.1", @@ -5307,9 +5307,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", - "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.1.tgz", + "integrity": "sha512-5RU2/lxTA3YUZxju61HO2U6EoZLvBLtmV2mbTvqyu4a/7s7RmJPT+1YekhMVsQhznRWk/czIwDUg+V8Q9ZuG4w==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index 94da758..ce7b0fd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matterbridge-zigbee2mqtt", - "version": "2.2.2-dev.6", + "version": "2.2.2", "description": "Matterbridge zigbee2mqtt plugin", "author": "https://github.com/Luligu", "license": "Apache-2.0", diff --git a/src/entity.ts b/src/entity.ts index 490b172..fd0c904 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -50,7 +50,6 @@ import { Endpoint, AtLeastOne, getClusterNameById, - DoorLockCluster, powerSource, bridgedNode, AirQuality, @@ -68,11 +67,9 @@ import { dimmableLight, colorTemperatureLight, onOffOutlet, - DescriptorCluster, - Descriptor, - ClusterServer, - VendorId, EndpointOptions, + SwitchesTag, + NumberTag, } from 'matterbridge'; import { AnsiLogger, TimestampFormat, gn, dn, ign, idn, rs, db, debugStringify, hk, zb, or, nf, LogLevel, CYAN, er } from 'matterbridge/logger'; import { deepCopy, deepEqual } from 'matterbridge/utils'; @@ -104,7 +101,7 @@ export class ZigbeeEntity extends EventEmitter { private lastSeen = 0; protected ignoreFeatures: string[] = []; protected transition = false; - protected propertyMap = new Map(); + protected propertyMap = new Map(); colorTimeout: NodeJS.Timeout | undefined = undefined; thermostatTimeout: NodeJS.Timeout | undefined = undefined; @@ -146,49 +143,51 @@ export class ZigbeeEntity extends EventEmitter { } if (this.bridgedDevice?.getClusterServerById(DoorLock.Cluster.id)) { this.log.info(`Configuring ${this.bridgedDevice?.deviceName} DoorLock cluster`); - const state = this.bridgedDevice?.getClusterServerById(DoorLock.Cluster.id)?.getLockStateAttribute(); + // const state = this.bridgedDevice?.getClusterServerById(DoorLock.Cluster.id)?.getLockStateAttribute(); + const state = this.bridgedDevice?.getAttribute(DoorLock.Cluster.id, 'lockState', this.log); if (this.bridgedDevice.number) { - if (state === DoorLock.LockState.Locked) this.bridgedDevice?.getClusterServer(DoorLockCluster)?.triggerLockOperationEvent({ lockOperationType: DoorLock.LockOperationType.Lock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }); - if (state === DoorLock.LockState.Unlocked) this.bridgedDevice?.getClusterServer(DoorLockCluster)?.triggerLockOperationEvent({ lockOperationType: DoorLock.LockOperationType.Unlock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }); + if (state === DoorLock.LockState.Locked) this.triggerEvent(DoorLock.Cluster.id, 'lockOperation', { lockOperationType: DoorLock.LockOperationType.Lock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }, this.log); + if (state === DoorLock.LockState.Unlocked) this.triggerEvent(DoorLock.Cluster.id, 'lockOperation', { lockOperationType: DoorLock.LockOperationType.Unlock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }, this.log); + // if (state === DoorLock.LockState.Locked) this.bridgedDevice?.getClusterServer(DoorLockCluster)?.triggerLockOperationEvent({ lockOperationType: DoorLock.LockOperationType.Lock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }); + // if (state === DoorLock.LockState.Unlocked) this.bridgedDevice?.getClusterServer(DoorLockCluster)?.triggerLockOperationEvent({ lockOperationType: DoorLock.LockOperationType.Unlock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }); } } } - // Use with this.addTagList(endpoint, null, NumberTag.One.namespaceId, NumberTag.One.tag); - // this.addTagList(endpoint, null, SwitchesTag.Custom.namespaceId, SwitchesTag.Custom.tag, 'Label'); - addTagList(endpoint: Endpoint, mfgCode: VendorId | null, namespaceId: number, tag: number, label?: string | null) { - const descriptor = endpoint.getClusterServer(DescriptorCluster.with(Descriptor.Feature.TagList)); - if (!descriptor) { - this.log.error(`addTagList: descriptor cluster not found on endpoint ${endpoint.name}:${endpoint.number}`); + /** + * Retrieves the value of the specified attribute from the given endpoint and cluster. + * + * @param {ClusterId} clusterId - The ID of the cluster to retrieve the attribute from. + * @param {string} event - The name of the event to trigger. + * @param {Record} payload - The payload of the event to trigger. + * @param {AnsiLogger} [log] - Optional logger for error and info messages. + * @param {Endpoint} [endpoint] - Optional the child endpoint to retrieve the attribute from. + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + triggerEvent(clusterId: ClusterId, event: string, payload: Record, log?: AnsiLogger, endpoint?: Endpoint) { + if (!endpoint) endpoint = this.bridgedDevice as Endpoint; + if (!endpoint.number) return; + + const clusterServer = endpoint.getClusterServerById(clusterId); + if (!clusterServer) { + log?.error(`triggerEvent error: Cluster ${clusterId} not found on endpoint ${endpoint.name}:${endpoint.number}`); return; } - - // tagList: { mfgCode: VendorId | null; namespaceId: number; tag: number; label?: string | null }[] = []; - - // We have already the feature, just add the new tag - if (descriptor.attributes.tagList) { - this.log.debug(`addTagList: adding ${CYAN}tagList${db} mfCode: ${mfgCode}, namespaceId: ${namespaceId}, tag: ${tag}, label: ${label} on endpoint $${endpoint.name}:${endpoint.number}`); - const tagList = descriptor.attributes.tagList.getLocal(); - tagList.push({ mfgCode, namespaceId, tag, label }); + const capitalizedEventName = event.charAt(0).toUpperCase() + event.slice(1); + if (!clusterServer.isEventSupportedByName(event) && !clusterServer.isEventSupportedByName(capitalizedEventName)) { + if (log) log.error(`triggerEvent error: Attribute ${event} not found on Cluster ${clusterServer.name} on endpoint ${endpoint.name}:${endpoint.number}`); return; } - - // We don't have the feature, we recreate descriptor with the tag - this.log.debug(`addTagList: creating ${CYAN}tagList${db} mfCode: ${mfgCode}, namespaceId: ${namespaceId}, tag: ${tag}, label: ${label} on endpoint $${endpoint.name}:${endpoint.number}`); - endpoint.addClusterServer( - ClusterServer( - DescriptorCluster.with(Descriptor.Feature.TagList), - { - tagList: [{ mfgCode, namespaceId, tag, label }], - deviceTypeList: [...descriptor.attributes.deviceTypeList.getLocal()], - serverList: [...descriptor.attributes.serverList.getLocal()], - clientList: [...descriptor.attributes.clientList.getLocal()], - partsList: [...descriptor.attributes.partsList.getLocal()], - }, - {}, - {}, - ), - ); + // Find the getter method + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if (!(clusterServer as any)[`trigger${capitalizedEventName}Event`]) { + log?.error(`triggerEvent error: Trigger trigger${capitalizedEventName}Event not found on Cluster ${clusterServer.name} on endpoint ${endpoint.name}:${endpoint.number}`); + return; + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-empty-object-type + const trigger = (clusterServer as any)[`trigger${capitalizedEventName}Event`] as (payload: Record) => {}; + trigger(payload); + log?.info(`${db}Trigger event ${hk}${clusterServer.name}.${capitalizedEventName}${db} on endpoint ${or}${endpoint.name}:${endpoint.number}${db}`); } constructor(platform: ZigbeePlatform, entity: BridgeDevice | BridgeGroup) { @@ -793,6 +792,9 @@ export class ZigbeeDevice extends ZigbeeEntity { const names: string[] = []; const properties: string[] = []; const units: string[] = []; + const value_mins: number[] = []; + const value_maxs: number[] = []; + const values: string[] = []; device.definition?.exposes.forEach((expose) => { if (expose.features) { // Specific features with type @@ -802,7 +804,10 @@ export class ZigbeeDevice extends ZigbeeEntity { endpoints.push(expose.endpoint || ''); names.push(feature.name); properties.push(feature.property); - units.push(feature.unit || ''); + units.push(feature.unit ?? ''); + value_mins.push(feature.value_min ?? NaN); + value_maxs.push(feature.value_max ?? NaN); + values.push(feature.values ? feature.values.join('|') : ''); }); } else { // Generic features without type @@ -812,7 +817,10 @@ export class ZigbeeDevice extends ZigbeeEntity { if (device.power_source === 'Battery' && expose.property === 'voltage') expose.property = 'battery_voltage'; names.push(expose.name || ''); properties.push(expose.property); - units.push(expose.unit || ''); + units.push(expose.unit ?? ''); + value_mins.push(expose.value_min ?? NaN); + value_maxs.push(expose.value_max ?? NaN); + values.push(expose.values ? expose.values.join('|') : ''); if (expose.name === 'action' && expose.values) { this.actions.push(...expose.values); } @@ -822,7 +830,10 @@ export class ZigbeeDevice extends ZigbeeEntity { types.push(''); names.push(option.name || ''); properties.push(option.property); - units.push(option.unit || ''); + units.push(option.unit ?? ''); + value_mins.push(option.value_min ?? NaN); + value_maxs.push(option.value_max ?? NaN); + values.push(option.values ? option.values.join('|') : ''); endpoints.push(option.endpoint || ''); }); if (platform.switchList.includes(device.friendly_name)) { @@ -869,16 +880,25 @@ export class ZigbeeDevice extends ZigbeeEntity { const endpoint = endpoints[index]; const property = properties[index]; const unit = units[index]; + const value_min = value_mins[index]; + const value_max = value_maxs[index]; + const value = values[index]; const z2m = z2ms.find((z2m) => z2m.type === type && z2m.name === name); if (z2m) { this.log.debug(`Device ${this.ien}${device.friendly_name}${rs}${db} endpoint: ${zb}${endpoint}${db} type: ${zb}${type}${db} property: ${zb}${name}${db} => deviceType: ${z2m.deviceType?.name} cluster: ${z2m.cluster} attribute: ${z2m.attribute}`); - this.propertyMap.set(property, { name, type, endpoint, unit }); + this.propertyMap.set(property, { name, type, endpoint, value_min, value_max, values: value, unit }); if (endpoint === '') { if (!this.bridgedDevice) this.bridgedDevice = this.createDevice([z2m.deviceType], [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)]); else this.bridgedDevice.addDeviceTypeWithClusterServer([z2m.deviceType], [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)]); } else { if (!this.bridgedDevice) this.bridgedDevice = this.createDevice([bridgedNode]); - /* const child = */ this.bridgedDevice.addChildDeviceTypeWithClusterServer(endpoint, [z2m.deviceType], [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)] /* , undefined, this.log.logLevel === LogLevel.DEBUG*/); + const child = this.bridgedDevice.addChildDeviceTypeWithClusterServer(endpoint, [z2m.deviceType], [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)], undefined, this.log.logLevel === LogLevel.DEBUG); + if (endpoint === 'l1') this.bridgedDevice.addTagList(child, null, NumberTag.One.namespaceId, NumberTag.One.tag, 'endpoint ' + endpoint); + if (endpoint === 'l2') this.bridgedDevice.addTagList(child, null, NumberTag.Two.namespaceId, NumberTag.Two.tag, 'endpoint ' + endpoint); + if (endpoint === 'l3') this.bridgedDevice.addTagList(child, null, NumberTag.Three.namespaceId, NumberTag.Three.tag, 'endpoint ' + endpoint); + if (endpoint === 'l4') this.bridgedDevice.addTagList(child, null, NumberTag.Four.namespaceId, NumberTag.Four.tag, 'endpoint ' + endpoint); + if (endpoint === 'l5') this.bridgedDevice.addTagList(child, null, NumberTag.Five.namespaceId, NumberTag.Five.tag, 'endpoint ' + endpoint); + if (endpoint === 'l6') this.bridgedDevice.addTagList(child, null, NumberTag.Six.namespaceId, NumberTag.Six.tag, 'endpoint ' + endpoint); /* if (endpoint === 'l1') this.addTagList(child, null, NumberTag.One.namespaceId, NumberTag.One.tag, 'endpoint ' + endpoint); if (endpoint === 'l1') this.addTagList(child, null, SwitchesTag.Custom.namespaceId, SwitchesTag.Custom.tag, 'endpoint ' + endpoint); @@ -909,7 +929,8 @@ export class ZigbeeDevice extends ZigbeeEntity { this.propertyMap.set('action_' + actionsMap[a], { name, type: '', endpoint: 'switch_' + count, action: triggerMap[a] }); this.log.info(`-- Button ${count}: ${hk}${switchMap[a]}${nf} <=> ${zb}${actionsMap[a]}${nf}`); } - this.bridgedDevice.addChildDeviceTypeWithClusterServer('switch_' + count, [DeviceTypes.GENERIC_SWITCH], [...DeviceTypes.GENERIC_SWITCH.requiredServerClusters] /* , undefined, this.log.logLevel === LogLevel.DEBUG*/); + const button = this.bridgedDevice.addChildDeviceTypeWithClusterServer('switch_' + count, [DeviceTypes.GENERIC_SWITCH], [...DeviceTypes.GENERIC_SWITCH.requiredServerClusters], undefined, this.log.logLevel === LogLevel.DEBUG); + this.bridgedDevice.addTagList(button, null, SwitchesTag.Custom.namespaceId, SwitchesTag.Custom.tag, 'switch_' + count); } else { for (let i = 0; i < this.actions.length; i += 3) { const actionsMap: string[] = []; @@ -918,7 +939,8 @@ export class ZigbeeDevice extends ZigbeeEntity { this.propertyMap.set('action_' + actionsMap[a - i], { name, type: '', endpoint: 'switch_' + count, action: triggerMap[a - i] }); this.log.info(`-- Button ${count}: ${hk}${switchMap[a - i]}${nf} <=> ${zb}${actionsMap[a - i]}${nf}`); } - this.bridgedDevice.addChildDeviceTypeWithClusterServer('switch_' + count, [DeviceTypes.GENERIC_SWITCH], [...DeviceTypes.GENERIC_SWITCH.requiredServerClusters] /* , undefined, this.log.logLevel === LogLevel.DEBUG*/); + const button = this.bridgedDevice.addChildDeviceTypeWithClusterServer('switch_' + count, [DeviceTypes.GENERIC_SWITCH], [...DeviceTypes.GENERIC_SWITCH.requiredServerClusters], undefined, this.log.logLevel === LogLevel.DEBUG); + this.bridgedDevice.addTagList(button, null, SwitchesTag.Custom.namespaceId, SwitchesTag.Custom.tag, 'switch_' + count); count++; } } @@ -976,14 +998,36 @@ export class ZigbeeDevice extends ZigbeeEntity { // Configure ColorControlCluster if (this.bridgedDevice && this.bridgedDevice.hasClusterServer(ColorControl.Complete)) { - this.log.debug(`Configuring device ${this.ien}${device.friendly_name}${rs}${db} ColorControlCluster with HS: ${names.includes('color_hs')} XY: ${names.includes('color_xy')} CT: ${names.includes('color_temp')}`); + this.log.debug(`Configuring device ${this.ien}${device.friendly_name}${rs}${db} ColorControlCluster cluster with HS: ${names.includes('color_hs')} XY: ${names.includes('color_xy')} CT: ${names.includes('color_temp')}`); this.bridgedDevice.configureColorControlCluster(names.includes('color_hs') || names.includes('color_xy'), false, names.includes('color_temp')); } + // Configure ThermostatCluster + if (this.bridgedDevice && this.bridgedDevice.hasClusterServer(Thermostat.Complete)) { + const heat = this.propertyMap.get('occupied_heating_setpoint') || this.propertyMap.get('current_heating_setpoint'); + const cool = this.propertyMap.get('occupied_cooling_setpoint') || this.propertyMap.get('current_cooling_setpoint'); + const minHeating = heat && heat.value_min !== undefined && !isNaN(heat.value_min) ? heat.value_min : 0; + const maxHeating = heat && heat.value_max !== undefined && !isNaN(heat.value_max) ? heat.value_max : 50; + const minCooling = cool && cool.value_min !== undefined && !isNaN(cool.value_min) ? cool.value_min : 0; + const maxCooling = cool && cool.value_max !== undefined && !isNaN(cool.value_max) ? cool.value_max : 50; + this.log.debug( + `Configuring device ${this.ien}${device.friendly_name}${rs}${db} Thermostat cluster with heating ${CYAN}${heat ? 'supported' : 'not supported'}${db} cooling ${CYAN}${cool ? 'supported' : 'not supported'}${db} ` + + `minHeating ${CYAN}${minHeating}${db} maxHeating ${CYAN}${maxHeating}${db} minCooling ${CYAN}${minCooling}${db} maxCooling ${CYAN}${maxCooling}${db}`, + ); + if (heat && !cool) { + this.propertyMap.delete('running_state'); // Remove running_state if only heating is supported cause it's not supported by the cluster without AutoMode + this.bridgedDevice.addClusterServer(this.bridgedDevice.getDefaultHeatingThermostatClusterServer(undefined, undefined, minHeating, maxHeating)); + } else if (!heat && cool) { + this.propertyMap.delete('running_state'); // Remove running_state if only cooling is supported cause it's not supported by the cluster without AutoMode + this.bridgedDevice.addClusterServer(this.bridgedDevice.getDefaultCoolingThermostatClusterServer(undefined, undefined, minCooling, maxCooling)); + } else if (heat && cool) { + this.bridgedDevice.addClusterServer(this.bridgedDevice.getDefaultThermostatClusterServer(undefined, undefined, undefined, undefined, minHeating, maxHeating, minCooling, maxCooling)); + } + } + /* Verify that all required server clusters are present in the main endpoint and in the child endpoints */ if (this.bridgedDevice) { - const deviceTypes = this.bridgedDevice.getDeviceTypes(); - deviceTypes.forEach((deviceType) => { + this.bridgedDevice.getDeviceTypes().forEach((deviceType) => { deviceType.requiredServerClusters.forEach((clusterId) => { if (!this.bridgedDevice) return; if (!this.bridgedDevice.getClusterServerById(clusterId)) { @@ -994,10 +1038,8 @@ export class ZigbeeDevice extends ZigbeeEntity { }); } if (this.bridgedDevice) { - const childEndpoints = this.bridgedDevice.getChildEndpoints(); - childEndpoints.forEach((childEndpoint) => { - const deviceTypes = childEndpoint.getDeviceTypes(); - deviceTypes.forEach((deviceType) => { + this.bridgedDevice.getChildEndpoints().forEach((childEndpoint) => { + childEndpoint.getDeviceTypes().forEach((deviceType) => { deviceType.requiredServerClusters.forEach((clusterId) => { if (!this.bridgedDevice) return; if (!childEndpoint.getClusterServerById(clusterId)) { @@ -1010,9 +1052,12 @@ export class ZigbeeDevice extends ZigbeeEntity { } if (!this.bridgedDevice) return; - // Properties + // Log properties this.propertyMap.forEach((value, key) => { - this.log.debug(`Property ${CYAN}${key}${db} name ${CYAN}${value.name}${db} type ${CYAN}${value.type === '' ? 'generic' : value.type}${db} endpoint ${CYAN}${value.endpoint === '' ? 'main' : value.endpoint}${db}`); + this.log.debug( + `Property ${CYAN}${key}${db} name ${CYAN}${value.name}${db} type ${CYAN}${value.type === '' ? 'generic' : value.type}${db} values ${CYAN}${value.values}${db} ` + + `value_min ${CYAN}${value.value_min}${db} value_max ${CYAN}${value.value_max}${db} unit ${CYAN}${value.unit}${db} endpoint ${CYAN}${value.endpoint === '' ? 'main' : value.endpoint}${db}`, + ); }); // Command handlers @@ -1198,24 +1243,26 @@ export class ZigbeeDevice extends ZigbeeEntity { this.noUpdate = false; }, 10 * 1000); }); - thermostat.subscribeOccupiedHeatingSetpointAttribute(async (value) => { - this.log.debug(`Subscribe occupiedHeatingSetpoint called for ${this.ien}${device.friendly_name}${rs}${db} with:`, value); - if (this.propertyMap.has('current_heating_setpoint')) this.publishCommand('OccupiedHeatingSetpoint', device.friendly_name, { current_heating_setpoint: Math.round(value / 100) }); - else if (this.propertyMap.has('occupied_heating_setpoint')) this.publishCommand('OccupiedHeatingSetpoint', device.friendly_name, { occupied_heating_setpoint: Math.round(value / 100) }); - if (this.bridgedDevice) this.noUpdate = true; - this.thermostatTimeout = setTimeout(() => { - if (this.bridgedDevice) this.noUpdate = false; - }, 10 * 1000); - }); - thermostat.subscribeOccupiedCoolingSetpointAttribute(async (value) => { - this.log.debug(`Subscribe occupiedCoolingSetpoint called for ${this.ien}${device.friendly_name}${rs}${db} with:`, value); - if (this.propertyMap.has('current_cooling_setpoint')) this.publishCommand('OccupiedCoolingSetpoint', device.friendly_name, { current_cooling_setpoint: Math.round(value / 100) }); - else if (this.propertyMap.has('occupied_cooling_setpoint')) this.publishCommand('OccupiedCoolingSetpoint', device.friendly_name, { occupied_cooling_setpoint: Math.round(value / 100) }); - if (this.bridgedDevice) this.noUpdate = true; - this.thermostatTimeout = setTimeout(() => { - if (this.bridgedDevice) this.noUpdate = false; - }, 10 * 1000); - }); + if (thermostat.subscribeOccupiedHeatingSetpointAttribute) + thermostat.subscribeOccupiedHeatingSetpointAttribute(async (value) => { + this.log.debug(`Subscribe occupiedHeatingSetpoint called for ${this.ien}${device.friendly_name}${rs}${db} with:`, value); + if (this.propertyMap.has('current_heating_setpoint')) this.publishCommand('OccupiedHeatingSetpoint', device.friendly_name, { current_heating_setpoint: Math.round(value / 100) }); + else if (this.propertyMap.has('occupied_heating_setpoint')) this.publishCommand('OccupiedHeatingSetpoint', device.friendly_name, { occupied_heating_setpoint: Math.round(value / 100) }); + if (this.bridgedDevice) this.noUpdate = true; + this.thermostatTimeout = setTimeout(() => { + if (this.bridgedDevice) this.noUpdate = false; + }, 10 * 1000); + }); + if (thermostat.subscribeOccupiedCoolingSetpointAttribute) + thermostat.subscribeOccupiedCoolingSetpointAttribute(async (value) => { + this.log.debug(`Subscribe occupiedCoolingSetpoint called for ${this.ien}${device.friendly_name}${rs}${db} with:`, value); + if (this.propertyMap.has('current_cooling_setpoint')) this.publishCommand('OccupiedCoolingSetpoint', device.friendly_name, { current_cooling_setpoint: Math.round(value / 100) }); + else if (this.propertyMap.has('occupied_cooling_setpoint')) this.publishCommand('OccupiedCoolingSetpoint', device.friendly_name, { occupied_cooling_setpoint: Math.round(value / 100) }); + if (this.bridgedDevice) this.noUpdate = true; + this.thermostatTimeout = setTimeout(() => { + if (this.bridgedDevice) this.noUpdate = false; + }, 10 * 1000); + }); } } } diff --git a/src/platform.ts b/src/platform.ts index 172421f..ce2936d 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -78,8 +78,8 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform { super(matterbridge, log, config); // Verify that Matterbridge is the correct version - if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('1.6.1')) { - throw new Error(`This plugin requires Matterbridge version >= "1.6.1". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend."`); + if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('1.6.2')) { + throw new Error(`This plugin requires Matterbridge version >= "1.6.2". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend."`); } // this.log.debug(`Config:')}${rs}`, config); From 7882d0425fb4e961892545e5abd8a10c88e0e35d Mon Sep 17 00:00:00 2001 From: Luligu Date: Mon, 25 Nov 2024 16:41:42 +0100 Subject: [PATCH 16/17] Release 2.2.2 --- package.json | 2 +- src/mock/bridge-devices.json | 919 ++++++----------------------------- src/mock/bridge-groups.json | 2 +- src/mock/bridge-info.json | 296 +++-------- 4 files changed, 199 insertions(+), 1020 deletions(-) diff --git a/package.json b/package.json index ce7b0fd..740037c 100644 --- a/package.json +++ b/package.json @@ -97,4 +97,4 @@ "typescript": "5.7.2", "typescript-eslint": "8.15.0" } -} \ No newline at end of file +} diff --git a/src/mock/bridge-devices.json b/src/mock/bridge-devices.json index fd325dd..642e0e1 100644 --- a/src/mock/bridge-devices.json +++ b/src/mock/bridge-devices.json @@ -77,14 +77,8 @@ "11": { "bindings": [], "clusters": { - "input": [ - "ssIasAce", - "genTime" - ], - "output": [ - "ssIasZone", - "ssIasWd" - ] + "input": ["ssIasAce", "genTime"], + "output": ["ssIasZone", "ssIasWd"] }, "configured_reportings": [], "scenes": [] @@ -101,9 +95,7 @@ "13": { "bindings": [], "clusters": { - "input": [ - "genOta" - ], + "input": ["genOta"], "output": [] }, "configured_reportings": [], @@ -277,19 +269,8 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genBasic", - "genIdentify", - "65535", - "msTemperatureMeasurement", - "msPressureMeasurement", - "msRelativeHumidity" - ], - "output": [ - "genBasic", - "genGroups", - "65535" - ] + "input": ["genBasic", "genIdentify", "65535", "msTemperatureMeasurement", "msPressureMeasurement", "msRelativeHumidity"], + "output": ["genBasic", "genGroups", "65535"] }, "configured_reportings": [], "scenes": [] @@ -404,17 +385,8 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genBasic", - "genIdentify", - "65535", - "genOnOff" - ], - "output": [ - "genBasic", - "genGroups", - "65535" - ] + "input": ["genBasic", "genIdentify", "65535", "genOnOff"], + "output": ["genBasic", "genGroups", "65535"] }, "configured_reportings": [], "scenes": [] @@ -527,16 +499,8 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genBasic", - "msIlluminanceMeasurement", - "genIdentify", - "genPowerCfg" - ], - "output": [ - "genIdentify", - "genOta" - ] + "input": ["genBasic", "msIlluminanceMeasurement", "genIdentify", "genPowerCfg"], + "output": ["genIdentify", "genOta"] }, "configured_reportings": [], "scenes": [] @@ -634,15 +598,8 @@ } ], "clusters": { - "input": [ - "genBasic", - "genIdentify", - "ssIasZone", - "genPowerCfg" - ], - "output": [ - "genIdentify" - ] + "input": ["genBasic", "genIdentify", "ssIasZone", "genPowerCfg"], + "output": ["genIdentify"] }, "configured_reportings": [ { @@ -819,19 +776,8 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genBasic", - "65535", - "msOccupancySensing", - "msIlluminanceMeasurement", - "ssIasZone", - "genPowerCfg", - "genIdentify" - ], - "output": [ - "genBasic", - "genOta" - ] + "input": ["genBasic", "65535", "msOccupancySensing", "msIlluminanceMeasurement", "ssIasZone", "genPowerCfg", "genIdentify"], + "output": ["genBasic", "genOta"] }, "configured_reportings": [], "scenes": [] @@ -890,11 +836,7 @@ "name": "motion_sensitivity", "property": "motion_sensitivity", "type": "enum", - "values": [ - "low", - "medium", - "high" - ] + "values": ["low", "medium", "high"] }, { "access": 7, @@ -1024,17 +966,8 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genBasic", - "genPowerCfg", - "genIdentify", - "manuSpecificLumi" - ], - "output": [ - "genIdentify", - "genOta", - "manuSpecificLumi" - ] + "input": ["genBasic", "genPowerCfg", "genIdentify", "manuSpecificLumi"], + "output": ["genIdentify", "genOta", "manuSpecificLumi"] }, "configured_reportings": [], "scenes": [] @@ -1151,10 +1084,7 @@ "name": "switch_type", "property": "switch_type", "type": "enum", - "values": [ - "toggle", - "momentary" - ] + "values": ["toggle", "momentary"] }, { "access": 1, @@ -1270,21 +1200,8 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genBasic", - "genDeviceTempCfg", - "genIdentify", - "genGroups", - "genScenes", - "genAlarms", - "genTime", - "manuSpecificLumi" - ], - "output": [ - "genTime", - "genOta", - "65535" - ] + "input": ["genBasic", "genDeviceTempCfg", "genIdentify", "genGroups", "genScenes", "genAlarms", "genTime", "manuSpecificLumi"], + "output": ["genTime", "genOta", "65535"] }, "configured_reportings": [], "scenes": [ @@ -1301,9 +1218,7 @@ "21": { "bindings": [], "clusters": { - "input": [ - "genAnalogInput" - ], + "input": ["genAnalogInput"], "output": [] }, "configured_reportings": [], @@ -1312,9 +1227,7 @@ "31": { "bindings": [], "clusters": { - "input": [ - "genAnalogInput" - ], + "input": ["genAnalogInput"], "output": [] }, "configured_reportings": [], @@ -1323,9 +1236,7 @@ "41": { "bindings": [], "clusters": { - "input": [ - "genMultistateInput" - ], + "input": ["genMultistateInput"], "output": [] }, "configured_reportings": [], @@ -1335,9 +1246,7 @@ "bindings": [], "clusters": { "input": [], - "output": [ - "greenPower" - ] + "output": ["greenPower"] }, "configured_reportings": [], "scenes": [] @@ -1453,11 +1362,7 @@ "name": "switch_type", "property": "switch_type", "type": "enum", - "values": [ - "toggle", - "momentary", - "none" - ] + "values": ["toggle", "momentary", "none"] }, { "access": 7, @@ -1467,12 +1372,7 @@ "name": "power_on_behavior", "property": "power_on_behavior", "type": "enum", - "values": [ - "on", - "previous", - "off", - "toggle" - ] + "values": ["on", "previous", "off", "toggle"] }, { "access": 7, @@ -1483,10 +1383,7 @@ "name": "operation_mode", "property": "operation_mode_l1", "type": "enum", - "values": [ - "decoupled", - "control_relay" - ] + "values": ["decoupled", "control_relay"] }, { "access": 7, @@ -1497,10 +1394,7 @@ "name": "operation_mode", "property": "operation_mode_l2", "type": "enum", - "values": [ - "decoupled", - "control_relay" - ] + "values": ["decoupled", "control_relay"] }, { "access": 7, @@ -1519,11 +1413,7 @@ "name": "mode", "property": "mode", "type": "enum", - "values": [ - "power", - "pulse", - "dry" - ] + "values": ["power", "pulse", "dry"] }, { "access": 7, @@ -1544,10 +1434,7 @@ "name": "action", "property": "action", "type": "enum", - "values": [ - "single_l1", - "single_l2" - ] + "values": ["single_l1", "single_l2"] }, { "access": 1, @@ -1672,21 +1559,8 @@ } ], "clusters": { - "input": [ - "haElectricalMeasurement", - "seMetering", - "genScenes", - "genGroups", - "genIdentify", - "genMultistateInput", - "genBasic", - "genOnOff", - "manuSpecificLumi" - ], - "output": [ - "genOta", - "genTime" - ] + "input": ["haElectricalMeasurement", "seMetering", "genScenes", "genGroups", "genIdentify", "genMultistateInput", "genBasic", "genOnOff", "manuSpecificLumi"], + "output": ["genOta", "genTime"] }, "configured_reportings": [ { @@ -1711,14 +1585,7 @@ } ], "clusters": { - "input": [ - "genScenes", - "genGroups", - "genIdentify", - "genMultistateInput", - "genOnOff", - "manuSpecificLumi" - ], + "input": ["genScenes", "genGroups", "genIdentify", "genMultistateInput", "genOnOff", "manuSpecificLumi"], "output": [] }, "configured_reportings": [ @@ -1735,9 +1602,7 @@ "21": { "bindings": [], "clusters": { - "input": [ - "genAnalogInput" - ], + "input": ["genAnalogInput"], "output": [] }, "configured_reportings": [], @@ -1768,11 +1633,7 @@ "name": "state", "property": "state", "type": "enum", - "values": [ - "OPEN", - "CLOSE", - "STOP" - ] + "values": ["OPEN", "CLOSE", "STOP"] }, { "access": 7, @@ -1803,11 +1664,7 @@ "name": "moving", "property": "moving", "type": "enum", - "values": [ - "UP", - "STOP", - "DOWN" - ] + "values": ["UP", "STOP", "DOWN"] }, { "access": 7, @@ -1871,17 +1728,8 @@ } ], "clusters": { - "input": [ - "genBasic", - "genGroups", - "genScenes", - "closuresWindowCovering", - "genOnOff" - ], - "output": [ - "genOta", - "genTime" - ] + "input": ["genBasic", "genGroups", "genScenes", "closuresWindowCovering", "genOnOff"], + "output": ["genOta", "genTime"] }, "configured_reportings": [ { @@ -1960,14 +1808,7 @@ "name": "action", "property": "action", "type": "enum", - "values": [ - "single", - "double", - "triple", - "quadruple", - "hold", - "release" - ] + "values": ["single", "double", "triple", "quadruple", "hold", "release"] }, { "access": 1, @@ -2011,14 +1852,8 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genBasic", - "genMultistateInput", - "genIdentify" - ], - "output": [ - "genBasic" - ] + "input": ["genBasic", "genMultistateInput", "genIdentify"], + "output": ["genBasic"] }, "configured_reportings": [], "scenes": [] @@ -2145,10 +1980,7 @@ "name": "system_mode", "property": "system_mode", "type": "enum", - "values": [ - "off", - "heat" - ] + "values": ["off", "heat"] }, { "access": 1, @@ -2157,11 +1989,7 @@ "name": "running_state", "property": "running_state", "type": "enum", - "values": [ - "idle", - "heat", - "cool" - ] + "values": ["idle", "heat", "cool"] }, { "access": 3, @@ -2170,10 +1998,7 @@ "name": "preset", "property": "preset", "type": "enum", - "values": [ - "hold", - "program" - ] + "values": ["hold", "program"] } ], "type": "climate" @@ -2186,11 +2011,7 @@ "name": "sensor", "property": "sensor", "type": "enum", - "values": [ - "IN", - "AL", - "OU" - ] + "values": ["IN", "AL", "OU"] }, { "access": 3, @@ -2585,16 +2406,8 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genGroups", - "genScenes", - "manuSpecificTuya", - "genBasic" - ], - "output": [ - "genOta", - "genTime" - ] + "input": ["genGroups", "genScenes", "manuSpecificTuya", "genBasic"], + "output": ["genOta", "genTime"] }, "configured_reportings": [], "scenes": [] @@ -2603,9 +2416,7 @@ "bindings": [], "clusters": { "input": [], - "output": [ - "greenPower" - ] + "output": ["greenPower"] }, "configured_reportings": [], "scenes": [] @@ -2671,11 +2482,7 @@ "name": "power_on_behavior", "property": "power_on_behavior", "type": "enum", - "values": [ - "off", - "previous", - "on" - ] + "values": ["off", "previous", "on"] }, { "access": 1, @@ -2720,19 +2527,8 @@ } ], "clusters": { - "input": [ - "genBasic", - "genIdentify", - "genGroups", - "genScenes", - "genOnOff", - "57344", - "manuSpecificTuya_3" - ], - "output": [ - "genOta", - "genTime" - ] + "input": ["genBasic", "genIdentify", "genGroups", "genScenes", "genOnOff", "57344", "manuSpecificTuya_3"], + "output": ["genOta", "genTime"] }, "configured_reportings": [ { @@ -2757,14 +2553,7 @@ } ], "clusters": { - "input": [ - "genIdentify", - "genGroups", - "genScenes", - "genOnOff", - "57344", - "manuSpecificTuya_3" - ], + "input": ["genIdentify", "genGroups", "genScenes", "genOnOff", "57344", "manuSpecificTuya_3"], "output": [] }, "configured_reportings": [ @@ -2782,9 +2571,7 @@ "bindings": [], "clusters": { "input": [], - "output": [ - "greenPower" - ] + "output": ["greenPower"] }, "configured_reportings": [], "scenes": [] @@ -2909,14 +2696,8 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genBasic", - "genIdentify", - "genPowerCfg" - ], - "output": [ - "genOta" - ] + "input": ["genBasic", "genIdentify", "genPowerCfg"], + "output": ["genOta"] }, "configured_reportings": [], "scenes": [] @@ -3101,16 +2882,7 @@ "name": "effect", "property": "effect", "type": "enum", - "values": [ - "blink", - "breathe", - "okay", - "channel_change", - "finish_effect", - "stop_effect", - "colorloop", - "stop_colorloop" - ] + "values": ["blink", "breathe", "okay", "channel_change", "finish_effect", "stop_effect", "colorloop", "stop_colorloop"] }, { "access": 1, @@ -3174,19 +2946,8 @@ } ], "clusters": { - "input": [ - "genBasic", - "genIdentify", - "genGroups", - "genScenes", - "genOnOff", - "genLevelCtrl", - "lightingColorCtrl", - "touchlink" - ], - "output": [ - "genOta" - ] + "input": ["genBasic", "genIdentify", "genGroups", "genScenes", "genOnOff", "genLevelCtrl", "lightingColorCtrl", "touchlink"], + "output": ["genOta"] }, "configured_reportings": [ { @@ -3203,9 +2964,7 @@ "bindings": [], "clusters": { "input": [], - "output": [ - "greenPower" - ] + "output": ["greenPower"] }, "configured_reportings": [], "scenes": [] @@ -3236,17 +2995,7 @@ "name": "action", "property": "action", "type": "enum", - "values": [ - "single", - "double", - "hold", - "brightness_move_to_level", - "color_temperature_move", - "brightness_step_up", - "brightness_step_down", - "on", - "off" - ] + "values": ["single", "double", "hold", "brightness_move_to_level", "color_temperature_move", "brightness_step_up", "brightness_step_down", "on", "off"] }, { "access": 1, @@ -3267,10 +3016,7 @@ "name": "operation_mode", "property": "operation_mode", "type": "enum", - "values": [ - "command", - "event" - ] + "values": ["command", "event"] }, { "access": 1, @@ -3342,24 +3088,8 @@ } ], "clusters": { - "input": [ - "genBasic", - "genPowerCfg", - "genIdentify", - "genGroups", - "genOnOff", - "touchlink", - "manuSpecificTuya_3" - ], - "output": [ - "genOta", - "genTime", - "genIdentify", - "genGroups", - "genOnOff", - "genLevelCtrl", - "touchlink" - ] + "input": ["genBasic", "genPowerCfg", "genIdentify", "genGroups", "genOnOff", "touchlink", "manuSpecificTuya_3"], + "output": ["genOta", "genTime", "genIdentify", "genGroups", "genOnOff", "genLevelCtrl", "touchlink"] }, "configured_reportings": [ { @@ -3405,15 +3135,7 @@ "name": "air_quality", "property": "air_quality", "type": "enum", - "values": [ - "excellent", - "good", - "moderate", - "poor", - "unhealthy", - "out_of_range", - "unknown" - ] + "values": ["excellent", "good", "moderate", "poor", "unhealthy", "out_of_range", "unknown"] }, { "access": 5, @@ -3534,11 +3256,7 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genIdentify", - "genScenes", - "genOnOff" - ], + "input": ["genIdentify", "genScenes", "genOnOff"], "output": [] }, "configured_reportings": [], @@ -3588,21 +3306,8 @@ } ], "clusters": { - "input": [ - "genBasic", - "genPowerCfg", - "genIdentify", - "genPollCtrl", - "msTemperatureMeasurement", - "msRelativeHumidity", - "1070", - "manuSpecificDevelcoAirQuality" - ], - "output": [ - "genIdentify", - "genTime", - "genOta" - ] + "input": ["genBasic", "genPowerCfg", "genIdentify", "genPollCtrl", "msTemperatureMeasurement", "msRelativeHumidity", "1070", "manuSpecificDevelcoAirQuality"], + "output": ["genIdentify", "genTime", "genOta"] }, "configured_reportings": [ { @@ -3688,10 +3393,7 @@ "name": "switch_type", "property": "switch_type", "type": "enum", - "values": [ - "toggle", - "momentary" - ] + "values": ["toggle", "momentary"] }, { "access": 1, @@ -3763,20 +3465,8 @@ } ], "clusters": { - "input": [ - "genBasic", - "genDeviceTempCfg", - "genIdentify", - "genGroups", - "genScenes", - "genOnOff", - "genMultistateInput", - "manuSpecificLumi" - ], - "output": [ - "genTime", - "genOta" - ] + "input": ["genBasic", "genDeviceTempCfg", "genIdentify", "genGroups", "genScenes", "genOnOff", "genMultistateInput", "manuSpecificLumi"], + "output": ["genTime", "genOta"] }, "configured_reportings": [ { @@ -3792,9 +3482,7 @@ "41": { "bindings": [], "clusters": { - "input": [ - "genMultistateInput" - ], + "input": ["genMultistateInput"], "output": [] }, "configured_reportings": [], @@ -3804,9 +3492,7 @@ "bindings": [], "clusters": { "input": [], - "output": [ - "greenPower" - ] + "output": ["greenPower"] }, "configured_reportings": [], "scenes": [] @@ -3865,11 +3551,7 @@ "name": "sensitivity", "property": "sensitivity", "type": "enum", - "values": [ - "low", - "medium", - "high" - ] + "values": ["low", "medium", "high"] }, { "access": 3, @@ -3878,12 +3560,7 @@ "name": "keep_time", "property": "keep_time", "type": "enum", - "values": [ - "10", - "30", - "60", - "120" - ] + "values": ["10", "30", "60", "120"] }, { "access": 3, @@ -3929,17 +3606,7 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genBasic", - "genIdentify", - "ssIasZone", - "manuSpecificTuya_2", - "manuSpecificTuya", - "60928", - "57344", - "genPowerCfg", - "msIlluminanceMeasurement" - ], + "input": ["genBasic", "genIdentify", "ssIasZone", "manuSpecificTuya_2", "manuSpecificTuya", "60928", "57344", "genPowerCfg", "msIlluminanceMeasurement"], "output": [] }, "configured_reportings": [], @@ -4058,16 +3725,7 @@ "name": "effect", "property": "effect", "type": "enum", - "values": [ - "blink", - "breathe", - "okay", - "channel_change", - "finish_effect", - "stop_effect", - "colorloop", - "stop_colorloop" - ] + "values": ["blink", "breathe", "okay", "channel_change", "finish_effect", "stop_effect", "colorloop", "stop_colorloop"] }, { "access": 3, @@ -4086,11 +3744,7 @@ "name": "color_power_on_behavior", "property": "color_power_on_behavior", "type": "enum", - "values": [ - "initial", - "previous", - "customized" - ] + "values": ["initial", "previous", "customized"] }, { "access": 1, @@ -4145,21 +3799,8 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genIdentify", - "genGroups", - "genScenes", - "genOnOff", - "touchlink", - "genLevelCtrl", - "lightingColorCtrl", - "manuSpecificTuya", - "genBasic" - ], - "output": [ - "genOta", - "genTime" - ] + "input": ["genIdentify", "genGroups", "genScenes", "genOnOff", "touchlink", "genLevelCtrl", "lightingColorCtrl", "manuSpecificTuya", "genBasic"], + "output": ["genOta", "genTime"] }, "configured_reportings": [], "scenes": [] @@ -4168,9 +3809,7 @@ "bindings": [], "clusters": { "input": [], - "output": [ - "greenPower" - ] + "output": ["greenPower"] }, "configured_reportings": [], "scenes": [] @@ -4354,16 +3993,7 @@ "name": "effect", "property": "effect", "type": "enum", - "values": [ - "blink", - "breathe", - "okay", - "channel_change", - "finish_effect", - "stop_effect", - "colorloop", - "stop_colorloop" - ] + "values": ["blink", "breathe", "okay", "channel_change", "finish_effect", "stop_effect", "colorloop", "stop_colorloop"] }, { "access": 7, @@ -4373,12 +4003,7 @@ "name": "power_on_behavior", "property": "power_on_behavior", "type": "enum", - "values": [ - "off", - "on", - "toggle", - "previous" - ] + "values": ["off", "on", "toggle", "previous"] }, { "access": 1, @@ -4433,18 +4058,8 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genBasic", - "genIdentify", - "genGroups", - "genScenes", - "genOnOff", - "genLevelCtrl", - "lightingColorCtrl" - ], - "output": [ - "genOta" - ] + "input": ["genBasic", "genIdentify", "genGroups", "genScenes", "genOnOff", "genLevelCtrl", "lightingColorCtrl"], + "output": ["genOta"] }, "configured_reportings": [], "scenes": [] @@ -4618,16 +4233,7 @@ "name": "effect", "property": "effect", "type": "enum", - "values": [ - "blink", - "breathe", - "okay", - "channel_change", - "finish_effect", - "stop_effect", - "colorloop", - "stop_colorloop" - ] + "values": ["blink", "breathe", "okay", "channel_change", "finish_effect", "stop_effect", "colorloop", "stop_colorloop"] }, { "access": 7, @@ -4637,12 +4243,7 @@ "name": "power_on_behavior", "property": "power_on_behavior", "type": "enum", - "values": [ - "off", - "on", - "toggle", - "previous" - ] + "values": ["off", "on", "toggle", "previous"] }, { "access": 1, @@ -4697,20 +4298,8 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genBasic", - "genIdentify", - "genGroups", - "genScenes", - "genOnOff", - "genLevelCtrl", - "lightingColorCtrl", - "touchlink", - "manuSpecificAmazonWWAH" - ], - "output": [ - "genOnOff" - ] + "input": ["genBasic", "genIdentify", "genGroups", "genScenes", "genOnOff", "genLevelCtrl", "lightingColorCtrl", "touchlink", "manuSpecificAmazonWWAH"], + "output": ["genOnOff"] }, "configured_reportings": [], "scenes": [] @@ -4718,14 +4307,8 @@ "3": { "bindings": [], "clusters": { - "input": [ - "65360", - "65361" - ], - "output": [ - "65360", - "65361" - ] + "input": ["65360", "65361"], + "output": ["65360", "65361"] }, "configured_reportings": [], "scenes": [] @@ -4734,9 +4317,7 @@ "bindings": [], "clusters": { "input": [], - "output": [ - "greenPower" - ] + "output": ["greenPower"] }, "configured_reportings": [], "scenes": [] @@ -4785,12 +4366,7 @@ "name": "self_test_result", "property": "self_test_result", "type": "enum", - "values": [ - "checking", - "success", - "failure", - "others" - ] + "values": ["checking", "success", "failure", "others"] }, { "access": 1, @@ -4846,16 +4422,8 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genGroups", - "genScenes", - "manuSpecificTuya", - "genBasic" - ], - "output": [ - "genOta", - "genTime" - ] + "input": ["genGroups", "genScenes", "manuSpecificTuya", "genBasic"], + "output": ["genOta", "genTime"] }, "configured_reportings": [], "scenes": [] @@ -4921,11 +4489,7 @@ "name": "sensitivity", "property": "sensitivity", "type": "enum", - "values": [ - "low", - "medium", - "high" - ] + "values": ["low", "medium", "high"] }, { "access": 1, @@ -5008,11 +4572,7 @@ "name": "action", "property": "action", "type": "enum", - "values": [ - "vibration", - "tilt", - "drop" - ] + "values": ["vibration", "tilt", "drop"] }, { "access": 1, @@ -5160,12 +4720,7 @@ "name": "motion_state", "property": "motion_state", "type": "enum", - "values": [ - "none", - "small", - "medium", - "large" - ] + "values": ["none", "small", "medium", "large"] }, { "access": 3, @@ -5269,16 +4824,7 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genBasic", - "genIdentify", - "ssIasZone", - "manuSpecificTuya_2", - "manuSpecificTuya", - "60928", - "57344", - "msIlluminanceMeasurement" - ], + "input": ["genBasic", "genIdentify", "ssIasZone", "manuSpecificTuya_2", "manuSpecificTuya", "60928", "57344", "msIlluminanceMeasurement"], "output": [] }, "configured_reportings": [], @@ -5288,9 +4834,7 @@ "bindings": [], "clusters": { "input": [], - "output": [ - "greenPower" - ] + "output": ["greenPower"] }, "configured_reportings": [], "scenes": [] @@ -5449,11 +4993,7 @@ "name": "power_on_behavior", "property": "power_on_behavior", "type": "enum", - "values": [ - "off", - "on", - "previous" - ] + "values": ["off", "on", "previous"] }, { "access": 3, @@ -5462,11 +5002,7 @@ "name": "backlight_mode", "property": "backlight_mode", "type": "enum", - "values": [ - "off", - "normal", - "inverted" - ] + "values": ["off", "normal", "inverted"] }, { "access": 1, @@ -5491,16 +5027,8 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genBasic", - "genGroups", - "genScenes", - "manuSpecificTuya" - ], - "output": [ - "genOta", - "genTime" - ] + "input": ["genBasic", "genGroups", "genScenes", "manuSpecificTuya"], + "output": ["genOta", "genTime"] }, "configured_reportings": [], "scenes": [] @@ -5651,16 +5179,8 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genGroups", - "genScenes", - "manuSpecificTuya", - "genBasic" - ], - "output": [ - "genOta", - "genTime" - ] + "input": ["genGroups", "genScenes", "manuSpecificTuya", "genBasic"], + "output": ["genOta", "genTime"] }, "configured_reportings": [], "scenes": [] @@ -5669,9 +5189,7 @@ "bindings": [], "clusters": { "input": [], - "output": [ - "greenPower" - ] + "output": ["greenPower"] }, "configured_reportings": [], "scenes": [] @@ -5847,11 +5365,7 @@ "name": "power_outage_memory", "property": "power_outage_memory", "type": "enum", - "values": [ - "on", - "off", - "restore" - ] + "values": ["on", "off", "restore"] }, { "access": 7, @@ -5860,12 +5374,7 @@ "name": "indicator_mode", "property": "indicator_mode", "type": "enum", - "values": [ - "off", - "off/on", - "on/off", - "on" - ] + "values": ["off", "off/on", "on/off", "on"] }, { "access": 1, @@ -6051,22 +5560,8 @@ } ], "clusters": { - "input": [ - "genBasic", - "genIdentify", - "genGroups", - "genScenes", - "genOnOff", - "seMetering", - "haElectricalMeasurement", - "msTemperatureMeasurement", - "57344", - "manuSpecificTuya_3" - ], - "output": [ - "genOta", - "genTime" - ] + "input": ["genBasic", "genIdentify", "genGroups", "genScenes", "genOnOff", "seMetering", "haElectricalMeasurement", "msTemperatureMeasurement", "57344", "manuSpecificTuya_3"], + "output": ["genOta", "genTime"] }, "configured_reportings": [ { @@ -6095,10 +5590,7 @@ "cluster": "seMetering", "maximum_report_interval": 3600, "minimum_report_interval": 5, - "reportable_change": [ - 1, - 1 - ] + "reportable_change": [1, 1] } ], "scenes": [] @@ -6107,9 +5599,7 @@ "bindings": [], "clusters": { "input": [], - "output": [ - "greenPower" - ] + "output": ["greenPower"] }, "configured_reportings": [], "scenes": [] @@ -6285,11 +5775,7 @@ "name": "power_outage_memory", "property": "power_outage_memory", "type": "enum", - "values": [ - "on", - "off", - "restore" - ] + "values": ["on", "off", "restore"] }, { "access": 7, @@ -6298,12 +5784,7 @@ "name": "indicator_mode", "property": "indicator_mode", "type": "enum", - "values": [ - "off", - "off/on", - "on/off", - "on" - ] + "values": ["off", "off/on", "on/off", "on"] }, { "access": 1, @@ -6489,22 +5970,8 @@ } ], "clusters": { - "input": [ - "genBasic", - "genIdentify", - "genGroups", - "genScenes", - "genOnOff", - "seMetering", - "haElectricalMeasurement", - "msTemperatureMeasurement", - "57344", - "manuSpecificTuya_3" - ], - "output": [ - "genOta", - "genTime" - ] + "input": ["genBasic", "genIdentify", "genGroups", "genScenes", "genOnOff", "seMetering", "haElectricalMeasurement", "msTemperatureMeasurement", "57344", "manuSpecificTuya_3"], + "output": ["genOta", "genTime"] }, "configured_reportings": [ { @@ -6533,10 +6000,7 @@ "cluster": "seMetering", "maximum_report_interval": 3600, "minimum_report_interval": 5, - "reportable_change": [ - 1, - 1 - ] + "reportable_change": [1, 1] } ], "scenes": [] @@ -6545,9 +6009,7 @@ "bindings": [], "clusters": { "input": [], - "output": [ - "greenPower" - ] + "output": ["greenPower"] }, "configured_reportings": [], "scenes": [] @@ -6731,16 +6193,7 @@ "name": "effect", "property": "effect", "type": "enum", - "values": [ - "blink", - "breathe", - "okay", - "channel_change", - "finish_effect", - "stop_effect", - "colorloop", - "stop_colorloop" - ] + "values": ["blink", "breathe", "okay", "channel_change", "finish_effect", "stop_effect", "colorloop", "stop_colorloop"] }, { "access": 1, @@ -6804,19 +6257,8 @@ } ], "clusters": { - "input": [ - "genBasic", - "genIdentify", - "genGroups", - "genScenes", - "genOnOff", - "genLevelCtrl", - "lightingColorCtrl", - "touchlink" - ], - "output": [ - "genOta" - ] + "input": ["genBasic", "genIdentify", "genGroups", "genScenes", "genOnOff", "genLevelCtrl", "lightingColorCtrl", "touchlink"], + "output": ["genOta"] }, "configured_reportings": [ { @@ -6833,9 +6275,7 @@ "bindings": [], "clusters": { "input": [], - "output": [ - "greenPower" - ] + "output": ["greenPower"] }, "configured_reportings": [], "scenes": [] @@ -6987,16 +6427,8 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genGroups", - "genScenes", - "manuSpecificTuya", - "genBasic" - ], - "output": [ - "genOta", - "genTime" - ] + "input": ["genGroups", "genScenes", "manuSpecificTuya", "genBasic"], + "output": ["genOta", "genTime"] }, "configured_reportings": [], "scenes": [] @@ -7005,9 +6437,7 @@ "bindings": [], "clusters": { "input": [], - "output": [ - "greenPower" - ] + "output": ["greenPower"] }, "configured_reportings": [], "scenes": [] @@ -7062,14 +6492,7 @@ "name": "effect", "property": "effect", "type": "enum", - "values": [ - "blink", - "breathe", - "okay", - "channel_change", - "finish_effect", - "stop_effect" - ] + "values": ["blink", "breathe", "okay", "channel_change", "finish_effect", "stop_effect"] }, { "access": 1, @@ -7114,18 +6537,8 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genBasic", - "genIdentify", - "genGroups", - "genScenes", - "genOnOff", - "genLevelCtrl", - "touchlink" - ], - "output": [ - "genOta" - ] + "input": ["genBasic", "genIdentify", "genGroups", "genScenes", "genOnOff", "genLevelCtrl", "touchlink"], + "output": ["genOta"] }, "configured_reportings": [], "scenes": [] @@ -7243,16 +6656,7 @@ "name": "effect", "property": "effect", "type": "enum", - "values": [ - "blink", - "breathe", - "okay", - "channel_change", - "finish_effect", - "stop_effect", - "colorloop", - "stop_colorloop" - ] + "values": ["blink", "breathe", "okay", "channel_change", "finish_effect", "stop_effect", "colorloop", "stop_colorloop"] }, { "access": 3, @@ -7271,11 +6675,7 @@ "name": "color_power_on_behavior", "property": "color_power_on_behavior", "type": "enum", - "values": [ - "initial", - "previous", - "customized" - ] + "values": ["initial", "previous", "customized"] }, { "access": 1, @@ -7330,21 +6730,8 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genIdentify", - "genGroups", - "genScenes", - "genOnOff", - "touchlink", - "genLevelCtrl", - "lightingColorCtrl", - "manuSpecificTuya", - "genBasic" - ], - "output": [ - "genOta", - "genTime" - ] + "input": ["genIdentify", "genGroups", "genScenes", "genOnOff", "touchlink", "genLevelCtrl", "lightingColorCtrl", "manuSpecificTuya", "genBasic"], + "output": ["genOta", "genTime"] }, "configured_reportings": [], "scenes": [] @@ -7353,9 +6740,7 @@ "bindings": [], "clusters": { "input": [], - "output": [ - "greenPower" - ] + "output": ["greenPower"] }, "configured_reportings": [], "scenes": [] @@ -7472,16 +6857,7 @@ "name": "effect", "property": "effect", "type": "enum", - "values": [ - "blink", - "breathe", - "okay", - "channel_change", - "finish_effect", - "stop_effect", - "colorloop", - "stop_colorloop" - ] + "values": ["blink", "breathe", "okay", "channel_change", "finish_effect", "stop_effect", "colorloop", "stop_colorloop"] }, { "access": 3, @@ -7500,11 +6876,7 @@ "name": "color_power_on_behavior", "property": "color_power_on_behavior", "type": "enum", - "values": [ - "initial", - "previous", - "customized" - ] + "values": ["initial", "previous", "customized"] }, { "access": 1, @@ -7559,21 +6931,8 @@ "1": { "bindings": [], "clusters": { - "input": [ - "genIdentify", - "genGroups", - "genScenes", - "genOnOff", - "touchlink", - "genLevelCtrl", - "lightingColorCtrl", - "manuSpecificTuya", - "genBasic" - ], - "output": [ - "genOta", - "genTime" - ] + "input": ["genIdentify", "genGroups", "genScenes", "genOnOff", "touchlink", "genLevelCtrl", "lightingColorCtrl", "manuSpecificTuya", "genBasic"], + "output": ["genOta", "genTime"] }, "configured_reportings": [], "scenes": [] @@ -7582,9 +6941,7 @@ "bindings": [], "clusters": { "input": [], - "output": [ - "greenPower" - ] + "output": ["greenPower"] }, "configured_reportings": [], "scenes": [] @@ -7601,4 +6958,4 @@ "supported": true, "type": "Router" } -] \ No newline at end of file +] diff --git a/src/mock/bridge-groups.json b/src/mock/bridge-groups.json index 7b5ef14..47ae746 100644 --- a/src/mock/bridge-groups.json +++ b/src/mock/bridge-groups.json @@ -120,4 +120,4 @@ } ] } -] \ No newline at end of file +] diff --git a/src/mock/bridge-info.json b/src/mock/bridge-info.json index ada54ef..6d3bb2d 100644 --- a/src/mock/bridge-info.json +++ b/src/mock/bridge-info.json @@ -11,16 +11,7 @@ "cache_state_send_on_startup": true, "channel": 20, "elapsed": false, - "ext_pan_id": [ - 61, - 200, - 45, - 154, - 163, - 42, - 44, - 146 - ], + "ext_pan_id": [61, 200, 45, 154, 163, 42, 44, 146], "homeassistant_legacy_entity_attributes": false, "last_seen": "ISO_8601_local", "legacy_api": false, @@ -31,9 +22,7 @@ "log_file": "log.log", "log_level": "info", "log_namespaced_levels": {}, - "log_output": [ - "console" - ], + "log_output": ["console"], "log_rotation": false, "log_symlink_current": false, "log_syslog": { @@ -248,36 +237,19 @@ "friendly_name": "Master motion" }, "7": { - "devices": [ - "0xa4c138363dff5c1d/1", - "0xa4c138071003495f/11", - "0x04cd15fffe0d0ebd/1", - "0xa4c1388ad0ebb0a6/1", - "0xa4c138a2a00bb8d4/1", - "0xa4c138e0a08604f5/1" - ], + "devices": ["0xa4c138363dff5c1d/1", "0xa4c138071003495f/11", "0x04cd15fffe0d0ebd/1", "0xa4c1388ad0ebb0a6/1", "0xa4c138a2a00bb8d4/1", "0xa4c138e0a08604f5/1"], "friendly_name": "Lights" }, "8": { - "devices": [ - "0x54ef44100086d51f/1", - "0x54ef4410009716a8/1", - "0x54ef4410009716a8/2", - "0xcc86ecfffe4e9d25/2", - "0xcc86ecfffe4e9d25/1" - ], + "devices": ["0x54ef44100086d51f/1", "0x54ef4410009716a8/1", "0x54ef4410009716a8/2", "0xcc86ecfffe4e9d25/2", "0xcc86ecfffe4e9d25/1"], "friendly_name": "Switches" }, "9": { - "devices": [ - "0x70ac08fffee6dd5e/1" - ], + "devices": ["0x70ac08fffee6dd5e/1"], "friendly_name": "Covers" }, "10": { - "devices": [ - "0xa4c138f402dfba8a/1" - ], + "devices": ["0xa4c138f402dfba8a/1"], "friendly_name": "Thermostats" } }, @@ -347,9 +319,7 @@ }, "debounce_ignore": { "description": "Protects unique payload values of specified payload properties from overriding within debounce time", - "examples": [ - "action" - ], + "examples": ["action"], "items": { "type": "string" }, @@ -364,11 +334,7 @@ }, "filtered_attributes": { "description": "Filter attributes with regex from published payload.", - "examples": [ - "^temperature$", - "^battery$", - "^action$" - ], + "examples": ["^temperature$", "^battery$", "^action$"], "items": { "type": "string" }, @@ -377,9 +343,7 @@ }, "filtered_cache": { "description": "Filter attributes with regex from being added to the cache, this prevents the attribute from being in the published payload when the value didn't change.", - "examples": [ - "^input_actions$" - ], + "examples": ["^input_actions$"], "items": { "type": "string" }, @@ -388,10 +352,7 @@ }, "filtered_optimistic": { "description": "Filter attributes with regex from optimistic publish payload when calling /set. (This has no effect if optimistic is set to false).", - "examples": [ - "^color_(mode|temp)$", - "color" - ], + "examples": ["^color_(mode|temp)$", "color"], "items": { "type": "string" }, @@ -413,10 +374,7 @@ } }, "title": "Home Assistant", - "type": [ - "object", - "null" - ] + "type": ["object", "null"] }, "icon": { "description": "The user-defined device icon for the frontend. It can be a full URL link to an image (e.g. https://SOME.SITE/MODEL123.jpg) (you cannot use a path to a local file) or base64 encoded data URL (e.g. image/svg+xml;base64,PHN2ZyB3aW....R0aD)", @@ -445,9 +403,7 @@ "type": "number" } }, - "required": [ - "friendly_name" - ], + "required": ["friendly_name"], "type": "object" }, "group": { @@ -470,15 +426,10 @@ "off_state": { "default": "auto", "description": "Control when to publish state OFF or CLOSE for a group. 'all_members_off': only publish state OFF/CLOSE when all group members are in state OFF/CLOSE, 'last_member_state': publish state OFF whenever one of its members changes to OFF", - "enum": [ - "all_members_off", - "last_member_state" - ], + "enum": ["all_members_off", "last_member_state"], "requiresRestart": true, "title": "Group off state", - "type": [ - "string" - ] + "type": ["string"] }, "optimistic": { "type": "boolean" @@ -490,9 +441,7 @@ "type": "boolean" } }, - "required": [ - "friendly_name" - ], + "required": ["friendly_name"], "type": "object" } }, @@ -505,10 +454,7 @@ "minimum": 1, "requiresRestart": true, "title": "Adapter concurrency", - "type": [ - "number", - "null" - ] + "type": ["number", "null"] }, "adapter_delay": { "description": "Adapter delay", @@ -516,10 +462,7 @@ "minimum": 0, "requiresRestart": true, "title": "Adapter delay", - "type": [ - "number", - "null" - ] + "type": ["number", "null"] }, "cache_state": { "default": true, @@ -542,11 +485,7 @@ "channel": { "default": 11, "description": "Zigbee channel, changing might require re-pairing some devices! (Note: use a ZLL channel: 11, 15, 20, or 25 to avoid problems)", - "examples": [ - 15, - 20, - 25 - ], + "examples": [15, 20, 25], "maximum": 26, "minimum": 11, "requiresRestart": true, @@ -580,12 +519,7 @@ "last_seen": { "default": "disable", "description": "Add a last_seen attribute to MQTT messages, contains date/time of last Zigbee message", - "enum": [ - "disable", - "ISO_8601", - "ISO_8601_local", - "epoch" - ], + "enum": ["disable", "ISO_8601", "ISO_8601_local", "epoch"], "title": "Last seen", "type": "string" }, @@ -606,10 +540,7 @@ "log_debug_namespace_ignore": { "default": "", "description": "Do not log these namespaces (regex-based) for debug level", - "examples": [ - "^zhc:legacy:fz:(tuya|moes)", - "^zhc:legacy:fz:(tuya|moes)|^zh:ember:uart:|^zh:controller" - ], + "examples": ["^zhc:legacy:fz:(tuya|moes)", "^zhc:legacy:fz:(tuya|moes)|^zh:ember:uart:|^zh:controller"], "title": "Log debug namespace ignore", "type": "string" }, @@ -622,9 +553,7 @@ }, "log_directory": { "description": "Location of log directory", - "examples": [ - "data/log/%TIMESTAMP%" - ], + "examples": ["data/log/%TIMESTAMP%"], "requiresRestart": true, "title": "Log directory", "type": "string" @@ -632,9 +561,7 @@ "log_file": { "default": "log.txt", "description": "Log file name, can also contain timestamp", - "examples": [ - "zigbee2mqtt_%TIMESTAMP%.log" - ], + "examples": ["zigbee2mqtt_%TIMESTAMP%.log"], "requiresRestart": true, "title": "Log file", "type": "string" @@ -642,24 +569,13 @@ "log_level": { "default": "info", "description": "Logging level", - "enum": [ - "error", - "warning", - "info", - "debug", - "warn" - ], + "enum": ["error", "warning", "info", "debug", "warn"], "title": "Log level", "type": "string" }, "log_namespaced_levels": { "additionalProperties": { - "enum": [ - "error", - "warning", - "info", - "debug" - ], + "enum": ["error", "warning", "info", "debug"], "type": "string" }, "default": {}, @@ -681,11 +597,7 @@ "log_output": { "description": "Output location of the log, leave empty to suppress logging", "items": { - "enum": [ - "console", - "file", - "syslog" - ], + "enum": ["console", "file", "syslog"], "type": "string" }, "requiresRestart": true, @@ -741,9 +653,7 @@ "path": { "default": "/dev/log", "description": "The path to the syslog dgram socket (i.e. /dev/log or /var/run/syslog for OS X).", - "examples": [ - "/var/run/syslog" - ], + "examples": ["/var/run/syslog"], "title": "Path", "type": "string" }, @@ -762,12 +672,7 @@ "protocol": { "default": "udp4", "description": "The network protocol to log over (e.g. tcp4, udp4, tls4, unix, unix-connect, etc).", - "examples": [ - "udp4", - "tls4", - "unix", - "unix-connect" - ], + "examples": ["udp4", "tls4", "unix", "unix-connect"], "title": "Protocol", "type": "string" }, @@ -804,11 +709,7 @@ }, "output": { "description": "Examples when 'state' of a device is published json: topic: 'zigbee2mqtt/my_bulb' payload '{\"state\": \"ON\"}' attribute: topic 'zigbee2mqtt/my_bulb/state' payload 'ON' attribute_and_json: both json and attribute (see above)", - "enum": [ - "attribute_and_json", - "attribute", - "json" - ], + "enum": ["attribute_and_json", "attribute", "json"], "title": "MQTT output type", "type": "string" }, @@ -829,9 +730,7 @@ }, "timestamp_format": { "description": "Log timestamp format", - "examples": [ - "YYYY-MM-DD HH:mm:ss" - ], + "examples": ["YYYY-MM-DD HH:mm:ss"], "requiresRestart": true, "title": "Timestamp format", "type": "string" @@ -842,10 +741,7 @@ "minimum": -128, "requiresRestart": true, "title": "Transmit power", - "type": [ - "number", - "null" - ] + "type": ["number", "null"] } }, "title": "Advanced", @@ -933,9 +829,7 @@ }, "external_converters": { "description": "You can define external converters to e.g. add support for a DiY device", - "examples": [ - "DIYRuZ_FreePad.js" - ], + "examples": ["DIYRuZ_FreePad.js"], "items": { "type": "string" }, @@ -955,10 +849,7 @@ "description": "Enables authentication, disabled by default", "requiresRestart": true, "title": "Auth token", - "type": [ - "string", - "null" - ] + "type": ["string", "null"] }, "base_url": { "default": "/", @@ -970,17 +861,10 @@ }, "host": { "description": "Frontend binding host. Binds to a unix socket when an absolute path is given instead.", - "examples": [ - "127.0.0.1", - "::1", - "/run/zigbee2mqtt/zigbee2mqtt.sock" - ], + "examples": ["127.0.0.1", "::1", "/run/zigbee2mqtt/zigbee2mqtt.sock"], "requiresRestart": true, "title": "Bind host", - "type": [ - "string", - "null" - ] + "type": ["string", "null"] }, "port": { "default": 8080, @@ -993,28 +877,19 @@ "description": "SSL Certificate file path for exposing HTTPS. The sibling property 'ssl_key' must be set for HTTPS to be activated.", "requiresRestart": true, "title": "Certificate file path", - "type": [ - "string", - "null" - ] + "type": ["string", "null"] }, "ssl_key": { "description": "SSL key file path for exposing HTTPS. The sibling property 'ssl_cert' must be set for HTTPS to be activated.", "requiresRestart": true, "title": "key file path", - "type": [ - "string", - "null" - ] + "type": ["string", "null"] }, "url": { "description": "URL on which the frontend can be reached, currently only used for the Home Assistant device configuration page", "requiresRestart": true, "title": "URL", - "type": [ - "string", - "null" - ] + "type": ["string", "null"] } }, "title": "Frontend (advanced)", @@ -1047,9 +922,7 @@ "properties": { "discovery_topic": { "description": "Home Assistant discovery topic", - "examples": [ - "homeassistant" - ], + "examples": ["homeassistant"], "requiresRestart": true, "title": "Homeassistant discovery topic", "type": "string" @@ -1068,9 +941,7 @@ }, "status_topic": { "description": "Home Assistant status topic", - "examples": [ - "homeassistant/status" - ], + "examples": ["homeassistant/status"], "requiresRestart": true, "title": "Home Assistant status topic", "type": "string" @@ -1143,36 +1014,28 @@ "base_topic": { "default": "zigbee2mqtt", "description": "MQTT base topic for Zigbee2MQTT MQTT messages", - "examples": [ - "zigbee2mqtt" - ], + "examples": ["zigbee2mqtt"], "requiresRestart": true, "title": "Base topic", "type": "string" }, "ca": { "description": "Absolute path to SSL/TLS certificate of CA used to sign server and client certificates", - "examples": [ - "/etc/ssl/mqtt-ca.crt" - ], + "examples": ["/etc/ssl/mqtt-ca.crt"], "requiresRestart": true, "title": "Certificate authority", "type": "string" }, "cert": { "description": "Absolute path to SSL/TLS certificate for client-authentication", - "examples": [ - "/etc/ssl/mqtt-client.crt" - ], + "examples": ["/etc/ssl/mqtt-client.crt"], "requiresRestart": true, "title": "SSL/TLS certificate", "type": "string" }, "client_id": { "description": "MQTT client ID", - "examples": [ - "MY_CLIENT_ID" - ], + "examples": ["MY_CLIENT_ID"], "requiresRestart": true, "title": "Client ID", "type": "string" @@ -1199,18 +1062,14 @@ }, "key": { "description": "Absolute path to SSL/TLS key for client-authentication", - "examples": [ - "/etc/ssl/mqtt-client.key" - ], + "examples": ["/etc/ssl/mqtt-client.key"], "requiresRestart": true, "title": "SSL/TLS key", "type": "string" }, "password": { "description": "MQTT server authentication password", - "examples": [ - "ILOVEPELMENI" - ], + "examples": ["ILOVEPELMENI"], "requiresRestart": true, "title": "Password", "type": "string" @@ -1224,18 +1083,14 @@ }, "server": { "description": "MQTT server URL (use mqtts:// for SSL/TLS connection)", - "examples": [ - "mqtt://localhost:1883" - ], + "examples": ["mqtt://localhost:1883"], "requiresRestart": true, "title": "MQTT server", "type": "string" }, "user": { "description": "MQTT server authentication user", - "examples": [ - "johnnysilverhand" - ], + "examples": ["johnnysilverhand"], "requiresRestart": true, "title": "User", "type": "string" @@ -1243,20 +1098,13 @@ "version": { "default": 4, "description": "MQTT protocol version", - "examples": [ - 5 - ], + "examples": [5], "requiresRestart": true, "title": "Version", - "type": [ - "number", - "null" - ] + "type": ["number", "null"] } }, - "required": [ - "server" - ], + "required": ["server"], "title": "MQTT", "type": "object" }, @@ -1283,15 +1131,10 @@ }, "zigbee_ota_override_index_location": { "description": "Location of override OTA index file", - "examples": [ - "index.json" - ], + "examples": ["index.json"], "requiresRestart": true, "title": "OTA index override file name", - "type": [ - "string", - "null" - ] + "type": ["string", "null"] } }, "title": "OTA updates", @@ -1317,28 +1160,14 @@ "adapter": { "default": "auto", "description": "Adapter type, not needed unless you are experiencing problems", - "enum": [ - "deconz", - "zstack", - "zigate", - "ezsp", - "auto", - "ember", - "zboss" - ], + "enum": ["deconz", "zstack", "zigate", "ezsp", "auto", "ember", "zboss"], "requiresRestart": true, "title": "Adapter", - "type": [ - "string" - ] + "type": ["string"] }, "baudrate": { "description": "Baud rate speed for serial port, this can be anything firmware support but default is 115200 for Z-Stack and EZSP, 38400 for Deconz, however note that some EZSP firmware need 57600", - "examples": [ - 38400, - 57600, - 115200 - ], + "examples": [38400, 57600, 115200], "requiresRestart": true, "title": "Baudrate", "type": "number" @@ -1352,15 +1181,10 @@ }, "port": { "description": "Location of the adapter. To autodetect the port, set null", - "examples": [ - "/dev/ttyACM0" - ], + "examples": ["/dev/ttyACM0"], "requiresRestart": true, "title": "Port", - "type": [ - "string", - "null" - ] + "type": ["string", "null"] }, "rtscts": { "description": "RTS / CTS Hardware Flow Control for serial port", @@ -1382,9 +1206,7 @@ "type": "array" } }, - "required": [ - "mqtt" - ], + "required": ["mqtt"], "type": "object" }, "coordinator": { @@ -1414,4 +1236,4 @@ "zigbee_herdsman_converters": { "version": "20.38.0" } -} \ No newline at end of file +} From 29584741adce650002501b13c04f630b95012389 Mon Sep 17 00:00:00 2001 From: Luligu Date: Tue, 26 Nov 2024 19:21:25 +0100 Subject: [PATCH 17/17] Release 2.2.2 --- CHANGELOG.md | 2 +- package-lock.json | 142 +++++++++++++++++++++---------------------- package.json | 8 +-- src/platform.test.ts | 50 ++++++++++++++- 4 files changed, 123 insertions(+), 79 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4afe9e8..87227e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ If you like this project and find it useful, please consider giving it a star on All notable changes to this project will be documented in this file. -## [2.2.2] - 2024-11-22 +## [2.2.2] - 2024-11-26 ### Added diff --git a/package-lock.json b/package-lock.json index 82c11c8..adf2063 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "moment": "2.30.1", - "mqtt": "5.10.2", + "mqtt": "5.10.3", "node-ansi-logger": "3.0.0", "node-persist-manager": "1.0.8" }, @@ -18,16 +18,16 @@ "@eslint/js": "9.15.0", "@types/eslint__js": "8.42.3", "@types/jest": "29.5.14", - "@types/node": "22.9.3", + "@types/node": "22.10.0", "eslint": "9.15.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-jest": "28.9.0", "eslint-plugin-prettier": "5.2.1", "jest": "29.7.0", - "prettier": "3.3.3", + "prettier": "3.4.1", "ts-jest": "29.2.5", "typescript": "5.7.2", - "typescript-eslint": "8.15.0" + "typescript-eslint": "8.16.0" }, "engines": { "node": ">=18.0.0" @@ -1438,12 +1438,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.9.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.3.tgz", - "integrity": "sha512-F3u1fs/fce3FFk+DAxbxc78DF8x0cY09RRL8GnXLmkJ1jvx3TtPdWoTT5/NiYfI5ASqXBmfqJi9dZ3gxMx4lzw==", + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", "license": "MIT", "dependencies": { - "undici-types": "~6.19.8" + "undici-types": "~6.20.0" } }, "node_modules/@types/readable-stream": { @@ -1490,17 +1490,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.15.0.tgz", - "integrity": "sha512-+zkm9AR1Ds9uLWN3fkoeXgFppaQ+uEVtfOV62dDmsy9QCNqlRHWNEck4yarvRNrvRcHQLGfqBNui3cimoz8XAg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.16.0.tgz", + "integrity": "sha512-5YTHKV8MYlyMI6BaEG7crQ9BhSc8RxzshOReKwZwRWN0+XvvTOm+L/UYLCYxFpfwYuAAqhxiq4yae0CMFwbL7Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.15.0", - "@typescript-eslint/type-utils": "8.15.0", - "@typescript-eslint/utils": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0", + "@typescript-eslint/scope-manager": "8.16.0", + "@typescript-eslint/type-utils": "8.16.0", + "@typescript-eslint/utils": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1524,16 +1524,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.15.0.tgz", - "integrity": "sha512-7n59qFpghG4uazrF9qtGKBZXn7Oz4sOMm8dwNWDQY96Xlm2oX67eipqcblDj+oY1lLCbf1oltMZFpUso66Kl1A==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.16.0.tgz", + "integrity": "sha512-D7DbgGFtsqIPIFMPJwCad9Gfi/hC0PWErRRHFnaCWoEDYi5tQUDiJCTmGUbBiLzjqAck4KcXt9Ayj0CNlIrF+w==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.15.0", - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/typescript-estree": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0", + "@typescript-eslint/scope-manager": "8.16.0", + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/typescript-estree": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0", "debug": "^4.3.4" }, "engines": { @@ -1553,14 +1553,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.15.0.tgz", - "integrity": "sha512-QRGy8ADi4J7ii95xz4UoiymmmMd/zuy9azCaamnZ3FM8T5fZcex8UfJcjkiEZjJSztKfEBe3dZ5T/5RHAmw2mA==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.16.0.tgz", + "integrity": "sha512-mwsZWubQvBki2t5565uxF0EYvG+FwdFb8bMtDuGQLdCCnGPrDEDvm1gtfynuKlnpzeBRqdFCkMf9jg1fnAK8sg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0" + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1571,14 +1571,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.15.0.tgz", - "integrity": "sha512-UU6uwXDoI3JGSXmcdnP5d8Fffa2KayOhUUqr/AiBnG1Gl7+7ut/oyagVeSkh7bxQ0zSXV9ptRh/4N15nkCqnpw==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.16.0.tgz", + "integrity": "sha512-IqZHGG+g1XCWX9NyqnI/0CX5LL8/18awQqmkZSl2ynn8F76j579dByc0jhfVSnSnhf7zv76mKBQv9HQFKvDCgg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.15.0", - "@typescript-eslint/utils": "8.15.0", + "@typescript-eslint/typescript-estree": "8.16.0", + "@typescript-eslint/utils": "8.16.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1599,9 +1599,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.15.0.tgz", - "integrity": "sha512-n3Gt8Y/KyJNe0S3yDCD2RVKrHBC4gTUcLTebVBXacPy091E6tNspFLKRXlk3hwT4G55nfr1n2AdFqi/XMxzmPQ==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.16.0.tgz", + "integrity": "sha512-NzrHj6thBAOSE4d9bsuRNMvk+BvaQvmY4dDglgkgGC0EW/tB3Kelnp3tAKH87GEwzoxgeQn9fNGRyFJM/xd+GQ==", "dev": true, "license": "MIT", "engines": { @@ -1613,14 +1613,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.15.0.tgz", - "integrity": "sha512-1eMp2JgNec/niZsR7ioFBlsh/Fk0oJbhaqO0jRyQBMgkz7RrFfkqF9lYYmBoGBaSiLnu8TAPQTwoTUiSTUW9dg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.16.0.tgz", + "integrity": "sha512-E2+9IzzXMc1iaBy9zmo+UYvluE3TW7bCGWSF41hVWUE01o8nzr1rvOQYSxelxr6StUvRcTMe633eY8mXASMaNw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0", + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1668,16 +1668,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.15.0.tgz", - "integrity": "sha512-k82RI9yGhr0QM3Dnq+egEpz9qB6Un+WLYhmoNcvl8ltMEededhh7otBVVIDDsEEttauwdY/hQoSsOv13lxrFzQ==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.16.0.tgz", + "integrity": "sha512-C1zRy/mOL8Pj157GiX4kaw7iyRLKfJXBR3L82hk5kS/GyHcOFmy4YUq/zfZti72I9wnuQtA/+xzft4wCC8PJdA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.15.0", - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/typescript-estree": "8.15.0" + "@typescript-eslint/scope-manager": "8.16.0", + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/typescript-estree": "8.16.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1696,13 +1696,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.15.0.tgz", - "integrity": "sha512-h8vYOulWec9LhpwfAdZf2bjr8xIp0KNKnpgqSz0qqYYKAW/QZKw3ktRndbiAtUz4acH4QLQavwZBYCc0wulA/Q==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.16.0.tgz", + "integrity": "sha512-pq19gbaMOmFE3CbL0ZB8J8BFCo2ckfHBfaIsaOZgBIF4EoISJIdLX5xRhd0FGB0LlHReNRuzoJoMGpTjq8F2CQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.15.0", + "@typescript-eslint/types": "8.16.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -2426,9 +2426,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.64", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.64.tgz", - "integrity": "sha512-IXEuxU+5ClW2IGEYFC2T7szbyVgehupCWQe5GNh+H065CD6U6IFN0s4KeAMFGNmQolRU4IV7zGBWSYMmZ8uuqQ==", + "version": "1.5.65", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.65.tgz", + "integrity": "sha512-PWVzBjghx7/wop6n22vS2MLU8tKGd4Q91aCEGhG/TYmW6PP5OcSXcdnxTe1NNt0T66N8D6jxh4kC8UsdzOGaIw==", "dev": true, "license": "ISC" }, @@ -4307,9 +4307,9 @@ } }, "node_modules/mqtt": { - "version": "5.10.2", - "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.10.2.tgz", - "integrity": "sha512-Q8NrMXB6FwQ2DulGONeDb6BtFHxyQHmXWzDrSC724iyofxLleq/wuZmztV3kg1Kda9I7l0oHP+FKesowoFxyUg==", + "version": "5.10.3", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.10.3.tgz", + "integrity": "sha512-hA/6YrUS4fywhBGCjH/XXUuLeueJiPqruVVWjK2A24Ma4KcWfZ/x8x07aoesBV+HXDWBC08tbT4IWfSXNW0Jtw==", "license": "MIT", "dependencies": { "@types/readable-stream": "^4.0.5", @@ -4718,9 +4718,9 @@ } }, "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz", + "integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==", "dev": true, "license": "MIT", "bin": { @@ -5307,9 +5307,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.1.tgz", - "integrity": "sha512-5RU2/lxTA3YUZxju61HO2U6EoZLvBLtmV2mbTvqyu4a/7s7RmJPT+1YekhMVsQhznRWk/czIwDUg+V8Q9ZuG4w==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.2.tgz", + "integrity": "sha512-ZF5gQIQa/UmzfvxbHZI3JXN0/Jt+vnAfAviNRAMc491laiK6YCLpCW9ft8oaCRFOTxCZtUTE6XB0ZQAe3olntw==", "dev": true, "license": "MIT", "engines": { @@ -5431,15 +5431,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.15.0.tgz", - "integrity": "sha512-wY4FRGl0ZI+ZU4Jo/yjdBu0lVTSML58pu6PgGtJmCufvzfV565pUF6iACQt092uFOd49iLOTX/sEVmHtbSrS+w==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.16.0.tgz", + "integrity": "sha512-wDkVmlY6O2do4V+lZd0GtRfbtXbeD0q9WygwXXSJnC1xorE8eqyC2L1tJimqpSeFrOzRlYtWnUp/uzgHQOgfBQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.15.0", - "@typescript-eslint/parser": "8.15.0", - "@typescript-eslint/utils": "8.15.0" + "@typescript-eslint/eslint-plugin": "8.16.0", + "@typescript-eslint/parser": "8.16.0", + "@typescript-eslint/utils": "8.16.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5458,9 +5458,9 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "license": "MIT" }, "node_modules/update-browserslist-db": { diff --git a/package.json b/package.json index 740037c..1b59eb3 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ }, "dependencies": { "moment": "2.30.1", - "mqtt": "5.10.2", + "mqtt": "5.10.3", "node-ansi-logger": "3.0.0", "node-persist-manager": "1.0.8" }, @@ -86,15 +86,15 @@ "@eslint/js": "9.15.0", "@types/eslint__js": "8.42.3", "@types/jest": "29.5.14", - "@types/node": "22.9.3", + "@types/node": "22.10.0", "eslint": "9.15.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-jest": "28.9.0", "eslint-plugin-prettier": "5.2.1", "jest": "29.7.0", - "prettier": "3.3.3", + "prettier": "3.4.1", "ts-jest": "29.2.5", "typescript": "5.7.2", - "typescript-eslint": "8.15.0" + "typescript-eslint": "8.16.0" } } diff --git a/src/platform.test.ts b/src/platform.test.ts index 4c72ab0..cf14db8 100644 --- a/src/platform.test.ts +++ b/src/platform.test.ts @@ -4,7 +4,7 @@ import { jest } from '@jest/globals'; -import { Matterbridge, PlatformConfig } from 'matterbridge'; +import { Matterbridge, MatterbridgeDevice, MatterbridgeEndpoint, PlatformConfig } from 'matterbridge'; import { AnsiLogger, db, idn, ign, LogLevel, rs, TimestampFormat, wr, debugStringify, or, hk, zb } from 'matterbridge/logger'; import { wait } from 'matterbridge/utils'; @@ -21,20 +21,39 @@ describe('TestPlatform', () => { let loggerLogSpy: jest.SpiedFunction<(level: LogLevel, message: string, ...parameters: any[]) => void>; let consoleLogSpy: jest.SpiedFunction<(...args: any[]) => void>; + let z2mStartSpy: jest.SpiedFunction<() => Promise>; let z2mStopSpy: jest.SpiedFunction<() => Promise>; let z2mSubscribeSpy: jest.SpiedFunction<(topic: string) => Promise>; let z2mPublishSpy: jest.SpiedFunction<(topic: string, message: string, queue: boolean) => Promise>; + const log = new AnsiLogger({ logName: 'ZigbeeTest', logTimestampFormat: TimestampFormat.TIME_MILLIS, logLevel: LogLevel.DEBUG }); beforeAll(() => { mockMatterbridge = { - addBridgedDevice: jest.fn(), + addBridgedDevice: jest.fn(async (pluginName: string, device: MatterbridgeDevice) => { + // console.error('addBridgedDevice called'); + }), + addBridgedEndpoint: jest.fn(async (pluginName: string, device: MatterbridgeEndpoint) => { + device.number = 100; + // console.error('addBridgedEndpoint called'); + }), + removeBridgedDevice: jest.fn(async (pluginName: string, device: MatterbridgeDevice) => { + // console.error('removeBridgedDevice called'); + }), + removeBridgedEndpoint: jest.fn(async (pluginName: string, device: MatterbridgeEndpoint) => { + // console.error('removeBridgedEndpoint called'); + }), + removeAllBridgedDevices: jest.fn(async (pluginName: string) => { + // console.error('removeAllBridgedDevices called'); + }), + removeAllBridgedEndpoints: jest.fn(async (pluginName: string) => { + // console.error('removeAllBridgedEndpoints called'); + }), matterbridgeDirectory: '', matterbridgePluginDirectory: 'temp', systemInformation: { ipv4Address: undefined }, matterbridgeVersion: '1.6.2', - removeAllBridgedDevices: jest.fn(), } as unknown as Matterbridge; mockConfig = { 'name': 'matterbridge-zigbee2mqtt', @@ -296,6 +315,31 @@ describe('TestPlatform', () => { await wait(200); }); + it('should add NewGroup', async () => { + const entity = 'NewGroup'; + const payload = { data: { friendly_name: entity, id: 15 }, status: 'ok', transaction: '8j6s7-10' }; + (z2mPlatform.z2m as any).messageHandler('zigbee2mqtt/bridge/response/group/add', Buffer.from(JSON.stringify(payload))); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringContaining(`zigbee2MQTT sent group_add friendly_name: ${entity} id ${payload.data.id} status ${payload.status}`)); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringContaining(`Registering group: ${entity}`)); + }); + + it('should unregister At home', async () => { + const entity = 'At home'; + const payload = { data: { force: false, id: entity }, status: 'ok', transaction: '8j6s7-10' }; + (z2mPlatform.z2m as any).messageHandler('zigbee2mqtt/bridge/response/group/remove', Buffer.from(JSON.stringify(payload))); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringContaining(`zigbee2MQTT sent group_remove friendly_name: ${payload.data.id} status ${payload.status}`)); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringContaining(`Removing device: ${entity}`)); + }); + + it('should rename Sleeping', async () => { + const entity = 'Sleeping'; + const payload = { data: { from: entity, to: 'Is dark' }, status: 'ok', transaction: '8j6s7-10' }; + (z2mPlatform.z2m as any).messageHandler('zigbee2mqtt/bridge/response/group/rename', Buffer.from(JSON.stringify(payload))); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringContaining(`zigbee2MQTT sent group_rename from: ${payload.data.from} to ${payload.data.to} status ${payload.status}`)); + expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringContaining(`Removing device: ${payload.data.from}`)); + // expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringContaining(`Registering group: ${payload.data.to}`)); + }); + it('should call onConfigure', async () => { await z2mPlatform.onConfigure(); expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, expect.stringMatching(/^Configured zigbee2mqtt dynamic platform/));