Compare commits

...
39 Commits
Author SHA1 Message Date
Koen Kanters 94751abaac chore(dev): release 2.10.1 (#31831) 2026-05-07 19:44:19 +02:00
Koen Kanters cc8456617a fix(ignore): update zigbee-herdsman-converters to 26.46.0 (#31937) 2026-05-07 17:36:55 +00:00
Koen Kanters 8727abdefc fix(ignore): update zigbee-herdsman-converters to 26.45.0 (#31925) 2026-05-06 20:05:18 +00:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 24e90273c9 fix(ignore): bump express-static-gzip from 3.0.0 to 3.0.1 in the minor-patch group (#31924)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-06 21:59:24 +02:00
Koen Kanters 845bdb7dba fix(ignore): update zigbee-herdsman-converters to 26.44.0 (#31900) 2026-05-05 18:57:33 +00:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 55f12ff520 fix(ignore): bump @biomejs/biome from 2.4.13 to 2.4.14 in the minor-patch group across 1 directory (#31881)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-05 20:30:06 +02:00
Koen Kanters 19eb05a0e0 fix(ignore): update zigbee-herdsman-converters to 26.43.0 (#31878) 2026-05-05 05:29:59 +00:00
Nerivec 9f7ea9b7c7 fix: Replace deprecated url.parse (#31845) 2026-05-04 20:29:24 +02:00
github-actions[bot] 0d7b8f189c chore: promote to dev 2026-05-01 17:40:55 +00:00
Koen Kanters 4639243cf9 chore(dev): release 2.10.0 (#31572) 2026-05-01 19:28:02 +02:00
Koen Kanters df26459b46 fix(ignore): update zigbee-herdsman-converters to 26.42.0 (#31830) 2026-05-01 15:30:23 +00:00
Koen Kanters c58162344f fix(ignore): update zigbee-herdsman-converters to 26.41.0 (#31820) 2026-04-30 19:56:07 +00:00
Andy 5974286445 fix: Improve transmit power description (#31735) 2026-04-28 22:13:25 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 4e527bbce8 fix(deps): bump googleapis/release-please-action from 4 to 5 (#31805)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-28 22:11:58 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> b3983b35de fix(ignore): bump the minor-patch group with 3 updates (#31806)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-28 22:11:47 +02:00
Koen KantersandMikhail Samin 429c5aea58 fix(ignore): update zigbee-herdsman-converters to 26.40.0 (#31800)
Co-authored-by: Mikhail Samin <i@mishasamin.xyz>
2026-04-26 20:05:21 +00:00
Koen Kanters e71507835b fix(ignore): update zigbee-herdsman-converters to 26.39.1 (#31783) 2026-04-23 19:24:07 +00:00
Koen Kanters 3bb3d56c05 fix(ignore): update zigbee-herdsman-converters to 26.38.1 (#31777) 2026-04-22 19:19:16 +00:00
Koen Kanters 305b7bcafa fix(ignore): update zigbee-herdsman-converters to 26.38.0 (#31768) 2026-04-21 19:39:41 +00:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 7ba232e27d fix(ignore): bump the minor-patch group with 2 updates (#31758)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-20 20:44:01 +02:00
Koen Kanters 78f440bada fix(ignore): update zigbee-herdsman-converters to 26.37.0 (#31749) 2026-04-19 19:39:12 +00:00
Koen Kanters a54775dea1 fix(ignore): update zigbee-herdsman-converters to 26.36.0 (#31737) 2026-04-17 18:01:27 +00:00
hoellenandKoen Kanters 041972669a feat: Home Assistant: add group entities in discovery config (#31663)
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2026-04-17 19:14:54 +02:00
Koen Kanters 0a7dfcef59 fix(ignore): update zigbee-herdsman-converters to 26.35.0 (#31713) 2026-04-14 19:09:08 +00:00
Koen Kanters 55d8d1f4d0 fix(ignore): update zigbee-herdsman-converters to 26.34.0 (#31702) 2026-04-13 19:19:40 +00:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> cdd7357522 fix(ignore): bump @biomejs/biome from 2.4.10 to 2.4.11 in the minor-patch group (#31699)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-13 21:00:46 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 4294512486 chore(deps): bump actions/github-script from 8 to 9 (#31697)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-13 21:00:32 +02:00
Koen Kanters 0c9ec722db fix(ignore): update zigbee-herdsman-converters to 26.33.1 (#31674) 2026-04-11 12:19:45 +00:00
Koen Kanters a0204ad3f0 fix(ignore): update zigbee-herdsman to 10.0.7 (#31673) 2026-04-11 11:32:03 +00:00
toastedcornflakes 4a63e65981 fix: Clarify units of pause_on_backoff_gt (#31668) 2026-04-11 12:47:09 +02:00
Koen Kanters 4818cc4163 fix(ignore): update zigbee-herdsman-converters to 26.32.0 (#31653) 2026-04-09 19:18:29 +00:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Koen Kanters
9e27181b64 fix(ignore): bump typescript from 5.9.3 to 6.0.2 (#31560)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2026-04-08 20:28:59 +02:00
Koen Kanters a50065cbb4 fix(ignore): update zigbee-herdsman-converters to 26.31.0 (#31637) 2026-04-07 19:14:49 +00:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> e6dd5c73bc fix(ignore): bump @types/node from 24.12.0 to 24.12.2 in the minor-patch group across 1 directory (#31628)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-07 20:55:57 +02:00
Koen Kanters aef3242319 fix(ignore): update zigbee-herdsman to 10.0.6 (#31636) 2026-04-07 18:31:15 +00:00
Koen Kanters 6b567c1286 fix(ignore): update zigbee-herdsman-converters to 26.30.0 (#31608) 2026-04-05 06:07:21 +00:00
Koen Kanters 23f9847994 fix(ignore): update zigbee-herdsman-converters to 26.29.0 (#31586) 2026-04-02 18:14:41 +00:00
Koen Kanters 08e4e9edeb fix(ignore): update zigbee-herdsman-converters to 26.28.0 (#31574) 2026-04-01 20:08:13 +00:00
github-actions[bot] 673d81f51a chore: promote to dev 2026-04-01 19:40:05 +00:00
15 changed files with 401 additions and 220 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
steps: steps:
- name: Delete untagged images - name: Delete untagged images
uses: actions/github-script@v8 uses: actions/github-script@v9
with: with:
github-token: ${{ secrets.GH_TOKEN }} github-token: ${{ secrets.GH_TOKEN }}
script: | script: |
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
path: zhc path: zhc
- name: Comment on new device support/external converter issue - name: Comment on new device support/external converter issue
uses: actions/github-script@v8 uses: actions/github-script@v9
with: with:
script: | script: |
const {newDeviceSupport} = await import("${{ github.workspace }}/z2m/scripts/issueBot.mjs") const {newDeviceSupport} = await import("${{ github.workspace }}/z2m/scripts/issueBot.mjs")
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
with: with:
node-version: 24 node-version: 24
- uses: googleapis/release-please-action@v4 - uses: googleapis/release-please-action@v5
id: release id: release
with: with:
target-branch: dev target-branch: dev
+1 -1
View File
@@ -1,3 +1,3 @@
{ {
".": "2.9.2" ".": "2.10.1"
} }
+50
View File
@@ -1,5 +1,55 @@
# Changelog # Changelog
## [2.10.1](https://github.com/Koenkk/zigbee2mqtt/compare/2.10.0...2.10.1) (2026-05-07)
### Bug Fixes
* **ignore:** bump @biomejs/biome from 2.4.13 to 2.4.14 in the minor-patch group across 1 directory ([#31881](https://github.com/Koenkk/zigbee2mqtt/issues/31881)) ([55f12ff](https://github.com/Koenkk/zigbee2mqtt/commit/55f12ff52063d576be0fe82891678c52181f3b8e))
* **ignore:** bump express-static-gzip from 3.0.0 to 3.0.1 in the minor-patch group ([#31924](https://github.com/Koenkk/zigbee2mqtt/issues/31924)) ([24e9027](https://github.com/Koenkk/zigbee2mqtt/commit/24e90273c9e712e122887e74ccf8208a8b42e762))
* **ignore:** update zigbee-herdsman-converters to 26.43.0 ([#31878](https://github.com/Koenkk/zigbee2mqtt/issues/31878)) ([19eb05a](https://github.com/Koenkk/zigbee2mqtt/commit/19eb05a0e023c7e2e8639c1dcda18399e96ae74d))
* **ignore:** update zigbee-herdsman-converters to 26.44.0 ([#31900](https://github.com/Koenkk/zigbee2mqtt/issues/31900)) ([845bdb7](https://github.com/Koenkk/zigbee2mqtt/commit/845bdb7dba5fe0f5e67e1b92c68872da0f3abf6a))
* **ignore:** update zigbee-herdsman-converters to 26.45.0 ([#31925](https://github.com/Koenkk/zigbee2mqtt/issues/31925)) ([8727abd](https://github.com/Koenkk/zigbee2mqtt/commit/8727abdefc9114a5e2e1036d764271c90065e0a1))
* **ignore:** update zigbee-herdsman-converters to 26.46.0 ([#31937](https://github.com/Koenkk/zigbee2mqtt/issues/31937)) ([cc84566](https://github.com/Koenkk/zigbee2mqtt/commit/cc8456617a4a389e7297e380901bfd6bb0223b32))
* Replace deprecated `url.parse` ([#31845](https://github.com/Koenkk/zigbee2mqtt/issues/31845)) ([9f7ea9b](https://github.com/Koenkk/zigbee2mqtt/commit/9f7ea9b7c79db7a781b431fbe43568e15647f8e5))
## [2.10.0](https://github.com/Koenkk/zigbee2mqtt/compare/2.9.2...2.10.0) (2026-05-01)
### Features
* Home Assistant: add group entities in discovery config ([#31663](https://github.com/Koenkk/zigbee2mqtt/issues/31663)) ([0419726](https://github.com/Koenkk/zigbee2mqtt/commit/041972669a6a8108e8fee1a0fffb555292371285))
### Bug Fixes
* Clarify units of pause_on_backoff_gt ([#31668](https://github.com/Koenkk/zigbee2mqtt/issues/31668)) ([4a63e65](https://github.com/Koenkk/zigbee2mqtt/commit/4a63e65981b6bbd746a730e7ca2291aba7a6c975))
* **deps:** bump googleapis/release-please-action from 4 to 5 ([#31805](https://github.com/Koenkk/zigbee2mqtt/issues/31805)) ([4e527bb](https://github.com/Koenkk/zigbee2mqtt/commit/4e527bbce8612c85fd636a1c493059af9793991f))
* **ignore:** bump @biomejs/biome from 2.4.10 to 2.4.11 in the minor-patch group ([#31699](https://github.com/Koenkk/zigbee2mqtt/issues/31699)) ([cdd7357](https://github.com/Koenkk/zigbee2mqtt/commit/cdd73575227527ae7eca5732d96687a30cea8538))
* **ignore:** bump @types/node from 24.12.0 to 24.12.2 in the minor-patch group across 1 directory ([#31628](https://github.com/Koenkk/zigbee2mqtt/issues/31628)) ([e6dd5c7](https://github.com/Koenkk/zigbee2mqtt/commit/e6dd5c73bc2d76b32c45ef19633626dbcdde8379))
* **ignore:** bump the minor-patch group with 2 updates ([#31758](https://github.com/Koenkk/zigbee2mqtt/issues/31758)) ([7ba232e](https://github.com/Koenkk/zigbee2mqtt/commit/7ba232e27d973119530119ca136aaab3e8e22ab6))
* **ignore:** bump the minor-patch group with 3 updates ([#31806](https://github.com/Koenkk/zigbee2mqtt/issues/31806)) ([b3983b3](https://github.com/Koenkk/zigbee2mqtt/commit/b3983b35de0b6242d8e4d01f0d1d1e535e2d0b60))
* **ignore:** bump typescript from 5.9.3 to 6.0.2 ([#31560](https://github.com/Koenkk/zigbee2mqtt/issues/31560)) ([9e27181](https://github.com/Koenkk/zigbee2mqtt/commit/9e27181b64b771250db283b5a4b1da7835470ee6))
* **ignore:** update zigbee-herdsman to 10.0.6 ([#31636](https://github.com/Koenkk/zigbee2mqtt/issues/31636)) ([aef3242](https://github.com/Koenkk/zigbee2mqtt/commit/aef32423195914aa9ef08875d4fe6de36df71f11))
* **ignore:** update zigbee-herdsman to 10.0.7 ([#31673](https://github.com/Koenkk/zigbee2mqtt/issues/31673)) ([a0204ad](https://github.com/Koenkk/zigbee2mqtt/commit/a0204ad3f00ed366e4c41455ae4189bd31870a95))
* **ignore:** update zigbee-herdsman-converters to 26.28.0 ([#31574](https://github.com/Koenkk/zigbee2mqtt/issues/31574)) ([08e4e9e](https://github.com/Koenkk/zigbee2mqtt/commit/08e4e9edeb048a07f89d6283e5bea04274a3fb47))
* **ignore:** update zigbee-herdsman-converters to 26.29.0 ([#31586](https://github.com/Koenkk/zigbee2mqtt/issues/31586)) ([23f9847](https://github.com/Koenkk/zigbee2mqtt/commit/23f9847994da94fa8b38ba823a8e55418b006e7c))
* **ignore:** update zigbee-herdsman-converters to 26.30.0 ([#31608](https://github.com/Koenkk/zigbee2mqtt/issues/31608)) ([6b567c1](https://github.com/Koenkk/zigbee2mqtt/commit/6b567c1286201aa9fef4744f7378798bc852c9ff))
* **ignore:** update zigbee-herdsman-converters to 26.31.0 ([#31637](https://github.com/Koenkk/zigbee2mqtt/issues/31637)) ([a50065c](https://github.com/Koenkk/zigbee2mqtt/commit/a50065cbb40bb149a87046bb83a7602a5587b3b0))
* **ignore:** update zigbee-herdsman-converters to 26.32.0 ([#31653](https://github.com/Koenkk/zigbee2mqtt/issues/31653)) ([4818cc4](https://github.com/Koenkk/zigbee2mqtt/commit/4818cc4163d578990339daf6c5e725bb40a08216))
* **ignore:** update zigbee-herdsman-converters to 26.33.1 ([#31674](https://github.com/Koenkk/zigbee2mqtt/issues/31674)) ([0c9ec72](https://github.com/Koenkk/zigbee2mqtt/commit/0c9ec722db3f53c57870de7f09de9971d69cbda8))
* **ignore:** update zigbee-herdsman-converters to 26.34.0 ([#31702](https://github.com/Koenkk/zigbee2mqtt/issues/31702)) ([55d8d1f](https://github.com/Koenkk/zigbee2mqtt/commit/55d8d1f4d0a5895ce146dbec98630d82adcd4e22))
* **ignore:** update zigbee-herdsman-converters to 26.35.0 ([#31713](https://github.com/Koenkk/zigbee2mqtt/issues/31713)) ([0a7dfce](https://github.com/Koenkk/zigbee2mqtt/commit/0a7dfcef596c3201f9f0cd81960c0e4ad8add0c4))
* **ignore:** update zigbee-herdsman-converters to 26.36.0 ([#31737](https://github.com/Koenkk/zigbee2mqtt/issues/31737)) ([a54775d](https://github.com/Koenkk/zigbee2mqtt/commit/a54775dea10a14b6d2f25ebb3c79a8a9d979e970))
* **ignore:** update zigbee-herdsman-converters to 26.37.0 ([#31749](https://github.com/Koenkk/zigbee2mqtt/issues/31749)) ([78f440b](https://github.com/Koenkk/zigbee2mqtt/commit/78f440bada9a14abb1d84af12fc77b1af7d250ea))
* **ignore:** update zigbee-herdsman-converters to 26.38.0 ([#31768](https://github.com/Koenkk/zigbee2mqtt/issues/31768)) ([305b7bc](https://github.com/Koenkk/zigbee2mqtt/commit/305b7bcafab59250173eddb9c08cb2fed0d966a0))
* **ignore:** update zigbee-herdsman-converters to 26.38.1 ([#31777](https://github.com/Koenkk/zigbee2mqtt/issues/31777)) ([3bb3d56](https://github.com/Koenkk/zigbee2mqtt/commit/3bb3d56c05b92e349d2b117973df6cf9414b8450))
* **ignore:** update zigbee-herdsman-converters to 26.39.1 ([#31783](https://github.com/Koenkk/zigbee2mqtt/issues/31783)) ([e715078](https://github.com/Koenkk/zigbee2mqtt/commit/e71507835b2c0673ec0df488e9d1db39a7b64a05))
* **ignore:** update zigbee-herdsman-converters to 26.40.0 ([#31800](https://github.com/Koenkk/zigbee2mqtt/issues/31800)) ([429c5ae](https://github.com/Koenkk/zigbee2mqtt/commit/429c5aea585da1525347adc13712bc556b4ea2ae))
* **ignore:** update zigbee-herdsman-converters to 26.41.0 ([#31820](https://github.com/Koenkk/zigbee2mqtt/issues/31820)) ([c581623](https://github.com/Koenkk/zigbee2mqtt/commit/c58162344fddc1cdb49027f6dcc9f1f35030bc61))
* **ignore:** update zigbee-herdsman-converters to 26.42.0 ([#31830](https://github.com/Koenkk/zigbee2mqtt/issues/31830)) ([df26459](https://github.com/Koenkk/zigbee2mqtt/commit/df26459b46e67f4f3b1079eb6fa6a439bf1979af))
* Improve transmit power description ([#31735](https://github.com/Koenkk/zigbee2mqtt/issues/31735)) ([5974286](https://github.com/Koenkk/zigbee2mqtt/commit/59742864457a9e29783363ebde8ede0e0aca5ea2))
## [2.9.2](https://github.com/Koenkk/zigbee2mqtt/compare/2.9.1...2.9.2) (2026-03-31) ## [2.9.2](https://github.com/Koenkk/zigbee2mqtt/compare/2.9.1...2.9.2) (2026-03-31)
+2 -3
View File
@@ -5,7 +5,6 @@ import {createServer} from "node:http";
import {createServer as createSecureServer} from "node:https"; import {createServer as createSecureServer} from "node:https";
import type {Socket} from "node:net"; import type {Socket} from "node:net";
import {posix} from "node:path"; import {posix} from "node:path";
import {parse} from "node:url";
import bind from "bind-decorator"; import bind from "bind-decorator";
import expressStaticGzip from "express-static-gzip"; import expressStaticGzip from "express-static-gzip";
import finalhandler from "finalhandler"; import finalhandler from "finalhandler";
@@ -157,10 +156,10 @@ export class Frontend extends Extension {
@bind private onUpgrade(request: IncomingMessage, socket: Socket, head: Buffer): void { @bind private onUpgrade(request: IncomingMessage, socket: Socket, head: Buffer): void {
this.wss.handleUpgrade(request, socket, head, (ws) => { this.wss.handleUpgrade(request, socket, head, (ws) => {
// biome-ignore lint/style/noNonNullAssertion: `Only valid for request obtained from Server` // biome-ignore lint/style/noNonNullAssertion: `Only valid for request obtained from Server`
const {query} = parse(request.url!, true); const {searchParams} = new URL(request.url!, "http://localhost"); // dummy base, may not be absolute
const authToken = settings.get().frontend.auth_token; const authToken = settings.get().frontend.auth_token;
if (!authToken || authToken === query.token) { if (!authToken || authToken === searchParams.get("token")) {
this.wss.emit("connection", ws, request); this.wss.emit("connection", ws, request);
} else { } else {
ws.close(4401, "Unauthorized"); ws.close(4401, "Unauthorized");
+98 -68
View File
@@ -2,7 +2,7 @@ import assert from "node:assert";
import bind from "bind-decorator"; import bind from "bind-decorator";
import stringify from "json-stable-stringify-without-jsonify"; import stringify from "json-stable-stringify-without-jsonify";
import type * as zhc from "zigbee-herdsman-converters"; import type * as zhc from "zigbee-herdsman-converters";
import type {Zh} from "zigbee-herdsman-converters/lib/types";
import logger from "../util/logger"; import logger from "../util/logger";
import * as settings from "../util/settings"; import * as settings from "../util/settings";
import utils, {assertBinaryExpose, assertEnumExpose, assertNumericExpose, isBinaryExpose, isEnumExpose, isNumericExpose} from "../util/utils"; import utils, {assertBinaryExpose, assertEnumExpose, assertNumericExpose, isBinaryExpose, isEnumExpose, isNumericExpose} from "../util/utils";
@@ -18,6 +18,7 @@ interface DiscoveryEntry {
type: string; type: string;
object_id: string; object_id: string;
discovery_payload: KeyValue; discovery_payload: KeyValue;
endpoint?: Zh.Endpoint;
} }
interface Discovered { interface Discovered {
@@ -384,6 +385,7 @@ export class HomeAssistant extends Extension {
private discoveryTopic: string; private discoveryTopic: string;
private discoveryRegex: RegExp; private discoveryRegex: RegExp;
private discoveryRegexWoTopic = /(.*)\/(.*)\/(.*)\/config/; private discoveryRegexWoTopic = /(.*)\/(.*)\/(.*)\/config/;
private groupMemberLookup = new Map<string, string>();
private statusTopic: string; private statusTopic: string;
private legacyActionSensor: boolean; private legacyActionSensor: boolean;
private experimentalEventEntities: boolean; private experimentalEventEntities: boolean;
@@ -459,10 +461,15 @@ export class HomeAssistant extends Extension {
const discoverWait = 5; const discoverWait = 5;
// Discover with `published = false`, this will populate `this.discovered` without publishing the discoveries. // Discover with `published = false`, this will populate `this.discovered` without publishing the discoveries.
// This is needed for clearing outdated entries in `this.onMQTTMessage()` // This is needed for clearing outdated entries in `this.onMQTTMessage()`
// Discover devices before groups to populate `this.groupMemberLookup` for group discovery.
await this.discover(this.bridge, false); await this.discover(this.bridge, false);
for (const e of this.zigbee.devicesAndGroupsIterator(utils.deviceNotCoordinator)) { for (const device of this.zigbee.devicesIterator(utils.deviceNotCoordinator)) {
await this.discover(e, false); await this.discover(device, false);
}
for (const group of this.zigbee.groupsIterator()) {
await this.discover(group, false);
} }
logger.debug(`Discovering entities to Home Assistant in ${discoverWait}s`); logger.debug(`Discovering entities to Home Assistant in ${discoverWait}s`);
@@ -487,21 +494,16 @@ export class HomeAssistant extends Extension {
return this.discovered[ID]; return this.discovered[ID];
} }
private exposeToConfig( private exposeToConfig(exposes: zhc.Expose[], entity: Device | Group, allExposes: zhc.Expose[]): DiscoveryEntry[] {
exposes: zhc.Expose[],
entityType: "device" | "group",
allExposes: zhc.Expose[],
definition?: zhc.Definition,
): DiscoveryEntry[] {
// For groups an array of exposes (of the same type) is passed, this is to determine e.g. what features // For groups an array of exposes (of the same type) is passed, this is to determine e.g. what features
// to use for a bulb (e.g. color_xy/color_temp) // to use for a bulb (e.g. color_xy/color_temp)
assert(entityType === "group" || exposes.length === 1, "Multiple exposes for device not allowed"); assert(entity.isGroup() || exposes.length === 1, "Multiple exposes for device not allowed");
const firstExpose = exposes[0]; const firstExpose = exposes[0];
assert(entityType === "device" || GROUP_SUPPORTED_TYPES.includes(firstExpose.type), `Unsupported expose type ${firstExpose.type} for group`); assert(entity.isDevice() || GROUP_SUPPORTED_TYPES.includes(firstExpose.type), `Unsupported expose type ${firstExpose.type} for group`);
const discoveryEntries: DiscoveryEntry[] = []; const discoveryEntries: DiscoveryEntry[] = [];
const endpoint = entityType === "device" ? exposes[0].endpoint : undefined; const endpointName = entity.isDevice() ? exposes[0].endpoint : undefined;
const getProperty = (feature: zhc.Feature): string => (entityType === "group" ? featurePropertyWithoutEndpoint(feature) : feature.property); const getProperty = (feature: zhc.Feature): string => (entity.isGroup() ? featurePropertyWithoutEndpoint(feature) : feature.property);
switch (firstExpose.type) { switch (firstExpose.type) {
case "light": { case "light": {
@@ -520,16 +522,16 @@ export class HomeAssistant extends Extension {
const discoveryEntry: DiscoveryEntry = { const discoveryEntry: DiscoveryEntry = {
type: "light", type: "light",
object_id: endpoint ? `light_${endpoint}` : "light", object_id: endpointName ? `light_${endpointName}` : "light",
mockProperties: [{property: state.property, value: null}], mockProperties: [{property: state.property, value: null}],
discovery_payload: { discovery_payload: {
name: endpoint ? utils.capitalize(endpoint) : null, name: endpointName ? utils.capitalize(endpointName) : null,
brightness: !!hasBrightness, brightness: !!hasBrightness,
schema: "json", schema: "json",
command_topic: true, command_topic: true,
brightness_scale: 254, brightness_scale: 254,
command_topic_prefix: endpoint, command_topic_prefix: endpointName,
state_topic_postfix: endpoint, state_topic_postfix: endpointName,
}, },
}; };
@@ -582,15 +584,15 @@ export class HomeAssistant extends Extension {
const property = getProperty(state); const property = getProperty(state);
const discoveryEntry: DiscoveryEntry = { const discoveryEntry: DiscoveryEntry = {
type: "switch", type: "switch",
object_id: endpoint ? `switch_${endpoint}` : "switch", object_id: endpointName ? `switch_${endpointName}` : "switch",
mockProperties: [{property: property, value: null}], mockProperties: [{property: property, value: null}],
discovery_payload: { discovery_payload: {
name: endpoint ? utils.capitalize(endpoint) : null, name: endpointName ? utils.capitalize(endpointName) : null,
payload_off: state.value_off, payload_off: state.value_off,
payload_on: state.value_on, payload_on: state.value_on,
value_template: `{{ value_json.${property} }}`, value_template: `{{ value_json.${property} }}`,
command_topic: true, command_topic: true,
command_topic_prefix: endpoint, command_topic_prefix: endpointName,
}, },
}; };
@@ -621,10 +623,10 @@ export class HomeAssistant extends Extension {
const discoveryEntry: DiscoveryEntry = { const discoveryEntry: DiscoveryEntry = {
type: "climate", type: "climate",
object_id: endpoint ? `climate_${endpoint}` : "climate", object_id: endpointName ? `climate_${endpointName}` : "climate",
mockProperties: [], mockProperties: [],
discovery_payload: { discovery_payload: {
name: endpoint ? utils.capitalize(endpoint) : null, name: endpointName ? utils.capitalize(endpointName) : null,
// Static // Static
state_topic: false, state_topic: false,
temperature_unit: "C", temperature_unit: "C",
@@ -635,7 +637,7 @@ export class HomeAssistant extends Extension {
// Temperature // Temperature
current_temperature_topic: true, current_temperature_topic: true,
current_temperature_template: `{{ value_json.${temperature.property} }}`, current_temperature_template: `{{ value_json.${temperature.property} }}`,
command_topic_prefix: endpoint, command_topic_prefix: endpointName,
}, },
}; };
@@ -704,13 +706,13 @@ export class HomeAssistant extends Extension {
if (tempCalibration) { if (tempCalibration) {
const discoveryEntry: DiscoveryEntry = { const discoveryEntry: DiscoveryEntry = {
type: "number", type: "number",
object_id: endpoint ? `${tempCalibration.name}_${endpoint}` : `${tempCalibration.name}`, object_id: endpointName ? `${tempCalibration.name}_${endpointName}` : `${tempCalibration.name}`,
mockProperties: [{property: tempCalibration.property, value: null}], mockProperties: [{property: tempCalibration.property, value: null}],
discovery_payload: { discovery_payload: {
name: endpoint ? `${tempCalibration.label} ${endpoint}` : tempCalibration.label, name: endpointName ? `${tempCalibration.label} ${endpointName}` : tempCalibration.label,
value_template: `{{ value_json.${tempCalibration.property} }}`, value_template: `{{ value_json.${tempCalibration.property} }}`,
command_topic: true, command_topic: true,
command_topic_prefix: endpoint, command_topic_prefix: endpointName,
command_topic_postfix: tempCalibration.property, command_topic_postfix: tempCalibration.property,
device_class: "temperature_delta", device_class: "temperature_delta",
entity_category: "config", entity_category: "config",
@@ -729,10 +731,10 @@ export class HomeAssistant extends Extension {
const piHeatingDemand = (firstExpose as zhc.Climate).features.filter(isNumericExpose).find((f) => f.name === "pi_heating_demand"); const piHeatingDemand = (firstExpose as zhc.Climate).features.filter(isNumericExpose).find((f) => f.name === "pi_heating_demand");
if (piHeatingDemand) { if (piHeatingDemand) {
const discoveryEntry: Partial<DiscoveryEntry> = { const discoveryEntry: Partial<DiscoveryEntry> = {
object_id: endpoint ? `${piHeatingDemand.name}_${endpoint}` : `${piHeatingDemand.name}`, object_id: endpointName ? `${piHeatingDemand.name}_${endpointName}` : `${piHeatingDemand.name}`,
mockProperties: [{property: piHeatingDemand.property, value: null}], mockProperties: [{property: piHeatingDemand.property, value: null}],
discovery_payload: { discovery_payload: {
name: endpoint ? `${piHeatingDemand.label} ${endpoint}` : piHeatingDemand.label, name: endpointName ? `${piHeatingDemand.label} ${endpointName}` : piHeatingDemand.label,
value_template: `{{ value_json.${piHeatingDemand.property} }}`, value_template: `{{ value_json.${piHeatingDemand.property} }}`,
...(piHeatingDemand.unit && {unit_of_measurement: piHeatingDemand.unit}), ...(piHeatingDemand.unit && {unit_of_measurement: piHeatingDemand.unit}),
icon: "mdi:radiator", icon: "mdi:radiator",
@@ -744,7 +746,7 @@ export class HomeAssistant extends Extension {
if (piHeatingDemand.access & ACCESS_SET) { if (piHeatingDemand.access & ACCESS_SET) {
discoveryEntry.type = "number"; discoveryEntry.type = "number";
discoveryEntry.discovery_payload.command_topic = true; discoveryEntry.discovery_payload.command_topic = true;
discoveryEntry.discovery_payload.command_topic_prefix = endpoint; discoveryEntry.discovery_payload.command_topic_prefix = endpointName;
discoveryEntry.discovery_payload.command_topic_postfix = piHeatingDemand.property; discoveryEntry.discovery_payload.command_topic_postfix = piHeatingDemand.property;
discoveryEntry.discovery_payload.min = piHeatingDemand.value_min; discoveryEntry.discovery_payload.min = piHeatingDemand.value_min;
discoveryEntry.discovery_payload.max = piHeatingDemand.value_max; discoveryEntry.discovery_payload.max = piHeatingDemand.value_max;
@@ -760,10 +762,10 @@ export class HomeAssistant extends Extension {
if (piCoolingDemand) { if (piCoolingDemand) {
const discoveryEntry: DiscoveryEntry = { const discoveryEntry: DiscoveryEntry = {
type: "sensor", type: "sensor",
object_id: endpoint ? /* v8 ignore next */ `${piCoolingDemand.name}_${endpoint}` : `${piCoolingDemand.name}`, object_id: endpointName ? /* v8 ignore next */ `${piCoolingDemand.name}_${endpointName}` : `${piCoolingDemand.name}`,
mockProperties: [{property: piCoolingDemand.property, value: null}], mockProperties: [{property: piCoolingDemand.property, value: null}],
discovery_payload: { discovery_payload: {
name: endpoint ? /* v8 ignore next */ `${piCoolingDemand.label} ${endpoint}` : piCoolingDemand.label, name: endpointName ? /* v8 ignore next */ `${piCoolingDemand.label} ${endpointName}` : piCoolingDemand.label,
value_template: `{{ value_json.${piCoolingDemand.property} }}`, value_template: `{{ value_json.${piCoolingDemand.property} }}`,
...(piCoolingDemand.unit && {unit_of_measurement: piCoolingDemand.unit}), ...(piCoolingDemand.unit && {unit_of_measurement: piCoolingDemand.unit}),
entity_category: "diagnostic", entity_category: "diagnostic",
@@ -782,10 +784,10 @@ export class HomeAssistant extends Extension {
if (localTemperature && !temperatureSensor && !localTemperatureSensor) { if (localTemperature && !temperatureSensor && !localTemperatureSensor) {
const discoveryEntry: DiscoveryEntry = { const discoveryEntry: DiscoveryEntry = {
type: "sensor", type: "sensor",
object_id: endpoint ? `${localTemperature.name}_${endpoint}` : `${localTemperature.name}`, object_id: endpointName ? `${localTemperature.name}_${endpointName}` : `${localTemperature.name}`,
mockProperties: [{property: localTemperature.property, value: null}], mockProperties: [{property: localTemperature.property, value: null}],
discovery_payload: { discovery_payload: {
name: endpoint ? `${localTemperature.label} ${endpoint}` : localTemperature.label, name: endpointName ? `${localTemperature.label} ${endpointName}` : localTemperature.label,
value_template: `{{ value_json.${localTemperature.property} }}`, value_template: `{{ value_json.${localTemperature.property} }}`,
...(localTemperature.unit && {unit_of_measurement: localTemperature.unit}), ...(localTemperature.unit && {unit_of_measurement: localTemperature.unit}),
device_class: "temperature", device_class: "temperature",
@@ -811,18 +813,18 @@ export class HomeAssistant extends Extension {
const discoveryEntry: DiscoveryEntry = { const discoveryEntry: DiscoveryEntry = {
type: "lock", type: "lock",
/* v8 ignore next */ /* v8 ignore next */
object_id: endpoint ? `lock_${endpoint}` : "lock", object_id: endpointName ? `lock_${endpointName}` : "lock",
mockProperties: [{property: state.property, value: null}], mockProperties: [{property: state.property, value: null}],
discovery_payload: { discovery_payload: {
/* v8 ignore next */ /* v8 ignore next */
name: endpoint ? utils.capitalize(endpoint) : null, name: endpointName ? utils.capitalize(endpointName) : null,
command_topic_prefix: endpoint, command_topic_prefix: endpointName,
command_topic: true, command_topic: true,
value_template: `{{ value_json.${state.property} }}`, value_template: `{{ value_json.${state.property} }}`,
state_locked: state.value_on, state_locked: state.value_on,
state_unlocked: state.value_off, state_unlocked: state.value_off,
/* v8 ignore next */ /* v8 ignore next */
command_topic_postfix: endpoint ? state.property : null, command_topic_postfix: endpointName ? state.property : null,
}, },
}; };
discoveryEntries.push(discoveryEntry); discoveryEntries.push(discoveryEntry);
@@ -847,13 +849,13 @@ export class HomeAssistant extends Extension {
const discoveryEntry: DiscoveryEntry = { const discoveryEntry: DiscoveryEntry = {
type: "cover", type: "cover",
mockProperties: [{property: state.property, value: null}], mockProperties: [{property: state.property, value: null}],
object_id: endpoint ? `cover_${endpoint}` : "cover", object_id: endpointName ? `cover_${endpointName}` : "cover",
discovery_payload: { discovery_payload: {
name: endpoint ? utils.capitalize(endpoint) : null, name: endpointName ? utils.capitalize(endpointName) : null,
command_topic_prefix: endpoint, command_topic_prefix: endpointName,
command_topic: true, command_topic: true,
state_topic: true, state_topic: true,
state_topic_postfix: endpoint, state_topic_postfix: endpointName,
}, },
}; };
@@ -916,7 +918,7 @@ export class HomeAssistant extends Extension {
break; break;
} }
case "fan": { case "fan": {
assert(!endpoint, "Endpoint not supported for fan type"); assert(!endpointName, "Endpoint not supported for fan type");
const discoveryEntry: DiscoveryEntry = { const discoveryEntry: DiscoveryEntry = {
type: "fan", type: "fan",
object_id: "fan", object_id: "fan",
@@ -952,7 +954,7 @@ export class HomeAssistant extends Extension {
); );
let presets = ["on", "auto", "smart"].filter((s) => modeEmulatedSpeed.values.includes(s)); let presets = ["on", "auto", "smart"].filter((s) => modeEmulatedSpeed.values.includes(s));
if (definition?.model === "99432") { if (entity.isDevice() && entity.definition?.model === "99432") {
// The Hampton Bay 99432 fan implements 4 speeds using the ZCL // The Hampton Bay 99432 fan implements 4 speeds using the ZCL
// hvacFanCtrl values `low`, `medium`, `high`, and `on`, and // hvacFanCtrl values `low`, `medium`, `high`, and `on`, and
// 1 preset called "Comfort Breeze" using the ZCL value `smart`. // 1 preset called "Comfort Breeze" using the ZCL value `smart`.
@@ -1014,9 +1016,9 @@ export class HomeAssistant extends Extension {
const discoveryEntry: DiscoveryEntry = { const discoveryEntry: DiscoveryEntry = {
type: "switch", type: "switch",
mockProperties: [{property: firstExpose.property, value: null}], mockProperties: [{property: firstExpose.property, value: null}],
object_id: endpoint ? `switch_${firstExpose.name}_${endpoint}` : `switch_${firstExpose.name}`, object_id: endpointName ? `switch_${firstExpose.name}_${endpointName}` : `switch_${firstExpose.name}`,
discovery_payload: { discovery_payload: {
name: endpoint ? /* v8 ignore next */ `${firstExpose.label} ${endpoint}` : firstExpose.label, name: endpointName ? /* v8 ignore next */ `${firstExpose.label} ${endpointName}` : firstExpose.label,
value_template: value_template:
typeof firstExpose.value_on === "boolean" typeof firstExpose.value_on === "boolean"
? `{% if value_json.${firstExpose.property} %}true{% else %}false{% endif %}` ? `{% if value_json.${firstExpose.property} %}true{% else %}false{% endif %}`
@@ -1024,7 +1026,7 @@ export class HomeAssistant extends Extension {
payload_on: firstExpose.value_on.toString(), payload_on: firstExpose.value_on.toString(),
payload_off: firstExpose.value_off.toString(), payload_off: firstExpose.value_off.toString(),
command_topic: true, command_topic: true,
command_topic_prefix: endpoint, command_topic_prefix: endpointName,
command_topic_postfix: firstExpose.property, command_topic_postfix: firstExpose.property,
...(BINARY_DISCOVERY_LOOKUP[firstExpose.name] || {}), ...(BINARY_DISCOVERY_LOOKUP[firstExpose.name] || {}),
}, },
@@ -1034,10 +1036,10 @@ export class HomeAssistant extends Extension {
} else { } else {
const discoveryEntry: DiscoveryEntry = { const discoveryEntry: DiscoveryEntry = {
type: "binary_sensor", type: "binary_sensor",
object_id: endpoint ? `${firstExpose.name}_${endpoint}` : `${firstExpose.name}`, object_id: endpointName ? `${firstExpose.name}_${endpointName}` : `${firstExpose.name}`,
mockProperties: [{property: firstExpose.property, value: null}], mockProperties: [{property: firstExpose.property, value: null}],
discovery_payload: { discovery_payload: {
name: endpoint ? /* v8 ignore next */ `${firstExpose.label} ${endpoint}` : firstExpose.label, name: endpointName ? /* v8 ignore next */ `${firstExpose.label} ${endpointName}` : firstExpose.label,
value_template: `{{ value_json.${firstExpose.property} }}`, value_template: `{{ value_json.${firstExpose.property} }}`,
payload_on: firstExpose.value_on, payload_on: firstExpose.value_on,
payload_off: firstExpose.value_off, payload_off: firstExpose.value_off,
@@ -1059,13 +1061,13 @@ export class HomeAssistant extends Extension {
if (allowsSet) { if (allowsSet) {
const discoveryEntry: DiscoveryEntry = { const discoveryEntry: DiscoveryEntry = {
type: "number", type: "number",
object_id: endpoint ? `${firstExpose.name}_${endpoint}` : `${firstExpose.name}`, object_id: endpointName ? `${firstExpose.name}_${endpointName}` : `${firstExpose.name}`,
mockProperties: [{property: firstExpose.property, value: null}], mockProperties: [{property: firstExpose.property, value: null}],
discovery_payload: { discovery_payload: {
name: endpoint ? `${firstExpose.label} ${endpoint}` : firstExpose.label, name: endpointName ? `${firstExpose.label} ${endpointName}` : firstExpose.label,
value_template: `{{ value_json.${firstExpose.property} }}`, value_template: `{{ value_json.${firstExpose.property} }}`,
command_topic: true, command_topic: true,
command_topic_prefix: endpoint, command_topic_prefix: endpointName,
command_topic_postfix: firstExpose.property, command_topic_postfix: firstExpose.property,
...(firstExpose.unit && {unit_of_measurement: firstExpose.unit}), ...(firstExpose.unit && {unit_of_measurement: firstExpose.unit}),
...(firstExpose.value_step && {step: firstExpose.value_step}), ...(firstExpose.value_step && {step: firstExpose.value_step}),
@@ -1110,10 +1112,10 @@ export class HomeAssistant extends Extension {
const discoveryEntry: DiscoveryEntry = { const discoveryEntry: DiscoveryEntry = {
type: "sensor", type: "sensor",
object_id: endpoint ? `${firstExpose.name}_${endpoint}` : `${firstExpose.name}`, object_id: endpointName ? `${firstExpose.name}_${endpointName}` : `${firstExpose.name}`,
mockProperties: [{property: firstExpose.property, value: null}], mockProperties: [{property: firstExpose.property, value: null}],
discovery_payload: { discovery_payload: {
name: endpoint ? `${firstExpose.label} ${endpoint}` : firstExpose.label, name: endpointName ? `${firstExpose.label} ${endpointName}` : firstExpose.label,
value_template: `{{ value_json.${firstExpose.property} }}`, value_template: `{{ value_json.${firstExpose.property} }}`,
enabled_by_default: !allowsSet, enabled_by_default: !allowsSet,
...(firstExpose.unit && {unit_of_measurement: firstExpose.unit}), ...(firstExpose.unit && {unit_of_measurement: firstExpose.unit}),
@@ -1155,7 +1157,7 @@ export class HomeAssistant extends Extension {
object_id: firstExpose.property, object_id: firstExpose.property,
mockProperties: [], mockProperties: [],
discovery_payload: { discovery_payload: {
name: endpoint ? /* v8 ignore next */ `${firstExpose.label} ${endpoint}` : firstExpose.label, name: endpointName ? /* v8 ignore next */ `${firstExpose.label} ${endpointName}` : firstExpose.label,
state_topic: true, state_topic: true,
event_types: this.prepareActionEventTypes(firstExpose.values), event_types: this.prepareActionEventTypes(firstExpose.values),
value_template: this.actionValueTemplate, value_template: this.actionValueTemplate,
@@ -1179,9 +1181,9 @@ export class HomeAssistant extends Extension {
object_id: firstExpose.property, object_id: firstExpose.property,
mockProperties: [{property: firstExpose.property, value: null}], mockProperties: [{property: firstExpose.property, value: null}],
discovery_payload: { discovery_payload: {
name: endpoint ? /* v8 ignore next */ `${firstExpose.label} ${endpoint}` : firstExpose.label, name: endpointName ? /* v8 ignore next */ `${firstExpose.label} ${endpointName}` : firstExpose.label,
state_topic: false, state_topic: false,
command_topic_prefix: endpoint, command_topic_prefix: endpointName,
command_topic: true, command_topic: true,
command_topic_postfix: firstExpose.property, command_topic_postfix: firstExpose.property,
payload_press: firstExpose.values[0].toString(), payload_press: firstExpose.values[0].toString(),
@@ -1200,10 +1202,10 @@ export class HomeAssistant extends Extension {
object_id: firstExpose.property, object_id: firstExpose.property,
mockProperties: [{property: firstExpose.property, value: null}], mockProperties: [{property: firstExpose.property, value: null}],
discovery_payload: { discovery_payload: {
name: endpoint ? `${firstExpose.label} ${endpoint}` : firstExpose.label, name: endpointName ? `${firstExpose.label} ${endpointName}` : firstExpose.label,
value_template: valueTemplate, value_template: valueTemplate,
state_topic: !!(firstExpose.access & ACCESS_STATE), state_topic: !!(firstExpose.access & ACCESS_STATE),
command_topic_prefix: endpoint, command_topic_prefix: endpointName,
command_topic: true, command_topic: true,
command_topic_postfix: firstExpose.property, command_topic_postfix: firstExpose.property,
options: firstExpose.values.map((v) => v.toString()), options: firstExpose.values.map((v) => v.toString()),
@@ -1222,7 +1224,7 @@ export class HomeAssistant extends Extension {
object_id: firstExpose.property, object_id: firstExpose.property,
mockProperties: [{property: firstExpose.property, value: null}], mockProperties: [{property: firstExpose.property, value: null}],
discovery_payload: { discovery_payload: {
name: endpoint ? `${firstExpose.label} ${endpoint}` : firstExpose.label, name: endpointName ? `${firstExpose.label} ${endpointName}` : firstExpose.label,
value_template: valueTemplate, value_template: valueTemplate,
...ENUM_DISCOVERY_LOOKUP[firstExpose.name], ...ENUM_DISCOVERY_LOOKUP[firstExpose.name],
}, },
@@ -1244,12 +1246,12 @@ export class HomeAssistant extends Extension {
const discoveryEntry: DiscoveryEntry = { const discoveryEntry: DiscoveryEntry = {
type: "siren", type: "siren",
object_id: endpoint ? /* v8 ignore next */ `siren_${endpoint}` : "siren", object_id: endpointName ? /* v8 ignore next */ `siren_${endpointName}` : "siren",
mockProperties: [{property: warningExpose.property, value: null}], mockProperties: [{property: warningExpose.property, value: null}],
discovery_payload: { discovery_payload: {
name: endpoint ? /* v8 ignore next */ utils.capitalize(endpoint) : null, name: endpointName ? /* v8 ignore next */ utils.capitalize(endpointName) : null,
command_topic: true, command_topic: true,
command_topic_prefix: endpoint, command_topic_prefix: endpointName,
state_topic: false, state_topic: false,
optimistic: true, optimistic: true,
}, },
@@ -1295,10 +1297,10 @@ export class HomeAssistant extends Extension {
object_id: firstExposeTyped.property, object_id: firstExposeTyped.property,
mockProperties: [{property: firstExposeTyped.property, value: null}], mockProperties: [{property: firstExposeTyped.property, value: null}],
discovery_payload: { discovery_payload: {
name: endpoint ? `${firstExposeTyped.label} ${endpoint}` : firstExposeTyped.label, name: endpointName ? `${firstExposeTyped.label} ${endpointName}` : firstExposeTyped.label,
state_topic: firstExposeTyped.access & ACCESS_STATE, state_topic: firstExposeTyped.access & ACCESS_STATE,
value_template: `{{ value_json.${firstExposeTyped.property} }}`, value_template: `{{ value_json.${firstExposeTyped.property} }}`,
command_topic_prefix: endpoint, command_topic_prefix: endpointName,
command_topic: true, command_topic: true,
command_topic_postfix: firstExposeTyped.property, command_topic_postfix: firstExposeTyped.property,
...LIST_DISCOVERY_LOOKUP[firstExposeTyped.name], ...LIST_DISCOVERY_LOOKUP[firstExposeTyped.name],
@@ -1312,7 +1314,7 @@ export class HomeAssistant extends Extension {
object_id: firstExposeTyped.property, object_id: firstExposeTyped.property,
mockProperties: [{property: firstExposeTyped.property, value: null}], mockProperties: [{property: firstExposeTyped.property, value: null}],
discovery_payload: { discovery_payload: {
name: endpoint ? `${firstExposeTyped.label} ${endpoint}` : firstExposeTyped.label, name: endpointName ? `${firstExposeTyped.label} ${endpointName}` : firstExposeTyped.label,
// Truncate text if it's too long // Truncate text if it's too long
// https://github.com/Koenkk/zigbee2mqtt/issues/23199 // https://github.com/Koenkk/zigbee2mqtt/issues/23199
value_template: `{{ value_json.${firstExposeTyped.property} | default('',True) | string | truncate(254, True, '', 0) }}`, value_template: `{{ value_json.${firstExposeTyped.property} | default('',True) | string | truncate(254, True, '', 0) }}`,
@@ -1349,6 +1351,8 @@ export class HomeAssistant extends Extension {
if (entry.discovery_payload.device_class) { if (entry.discovery_payload.device_class) {
delete entry.discovery_payload.name; delete entry.discovery_payload.name;
} }
entry.endpoint = entity.isDevice() ? entity.endpoint(endpointName) : undefined;
} }
return discoveryEntries; return discoveryEntries;
@@ -1474,7 +1478,7 @@ export class HomeAssistant extends Extension {
if (isDevice) { if (isDevice) {
const exposes = entity.exposes(); // avoid calling it hundred of times/s const exposes = entity.exposes(); // avoid calling it hundred of times/s
for (const expose of exposes) { for (const expose of exposes) {
configs.push(...this.exposeToConfig([expose], "device", exposes, entity.definition)); configs.push(...this.exposeToConfig([expose], entity, exposes));
} }
} else if (isGroup) { } else if (isGroup) {
// group // group
@@ -1503,7 +1507,7 @@ export class HomeAssistant extends Extension {
} }
configs = ([] as DiscoveryEntry[]).concat( configs = ([] as DiscoveryEntry[]).concat(
...Object.values(exposesByType).map((exposes) => this.exposeToConfig(exposes, "group", allExposes)), ...Object.values(exposesByType).map((exposes) => this.exposeToConfig(exposes, entity, allExposes)),
); );
} else { } else {
// Discover bridge config. // Discover bridge config.
@@ -1656,7 +1660,33 @@ export class HomeAssistant extends Extension {
payload.default_entity_id = `${config.type}.${payload.object_id}`; payload.default_entity_id = `${config.type}.${payload.object_id}`;
// Set unique_id // Set unique_id
payload.unique_id = `${entity.options.ID}_${config.object_id}_${settings.get().mqtt.base_topic}`; const uniqueId = `${entity.options.ID}_${config.object_id}_${settings.get().mqtt.base_topic}`;
payload.unique_id = uniqueId;
if (config.endpoint) {
assert(entity.isDevice());
const memberKey = `${config.endpoint.deviceIeeeAddress}_${config.endpoint.ID}_${config.type}`;
this.groupMemberLookup.set(memberKey, uniqueId);
}
// Add group member unique_ids for Home Assistant entity grouping
// This assumes that `discover()` already has been called for all devices, since it depends on the
// `groupMemberLookup` being populated.
// https://www.home-assistant.io/integrations/mqtt#grouping-entities
if (isGroup) {
const memberIds: string[] = [];
for (const endpoint of entity.zh.members) {
const memberKey = `${endpoint.deviceIeeeAddress}_${endpoint.ID}_${config.type}`;
const memberUniqueId = this.groupMemberLookup.get(memberKey);
if (memberUniqueId) {
memberIds.push(memberUniqueId);
}
}
if (memberIds.length > 0) {
payload.group = memberIds;
}
}
// Attributes for device registry and origin // Attributes for device registry and origin
payload.device = devicePayload; payload.device = devicePayload;
+2 -2
View File
@@ -88,7 +88,7 @@
"title": "Pause on backoff greater than", "title": "Pause on backoff greater than",
"default": 0, "default": 0,
"minimum": 0, "minimum": 0,
"description": "Pause availability pings when backoff reaches over this limit until a new Zigbee message is received from the device. A value of zero disables pausing." "description": "Pause availability pings when the backoff multiplier reaches over this limit until a new Zigbee message is received from the device. A value of zero disables pausing."
} }
}, },
"required": ["timeout"] "required": ["timeout"]
@@ -800,7 +800,7 @@
"requiresRestart": true, "requiresRestart": true,
"minimum": -128, "minimum": -128,
"maximum": 127, "maximum": 127,
"description": "Transmit power of adapter, only available for Z-Stack (CC253*/CC2652/CC1352) adapters, CC2652 = 5dbm, CC1352 max is = 20dbm (5dbm default)" "description": "Transmit power of adapter, in dBm (max is often 20, refer to chip specifications)"
}, },
"output": { "output": {
"type": "string", "type": "string",
+10 -10
View File
@@ -1,6 +1,6 @@
{ {
"name": "zigbee2mqtt", "name": "zigbee2mqtt",
"version": "2.9.2", "version": "2.10.1",
"description": "Zigbee to MQTT bridge using Zigbee-herdsman", "description": "Zigbee to MQTT bridge using Zigbee-herdsman",
"main": "index.js", "main": "index.js",
"types": "dist/types/api.d.ts", "types": "dist/types/api.d.ts",
@@ -42,10 +42,10 @@
}, },
"homepage": "https://koenkk.github.io/zigbee2mqtt", "homepage": "https://koenkk.github.io/zigbee2mqtt",
"dependencies": { "dependencies": {
"ajv": "^8.18.0", "ajv": "^8.20.0",
"bind-decorator": "^1.0.11", "bind-decorator": "^1.0.11",
"debounce": "^3.0.0", "debounce": "^3.0.0",
"express-static-gzip": "^3.0.0", "express-static-gzip": "^3.0.1",
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"finalhandler": "^2.1.1", "finalhandler": "^2.1.1",
"humanize-duration": "^3.33.2", "humanize-duration": "^3.33.2",
@@ -62,24 +62,24 @@
"winston-syslog": "^2.7.1", "winston-syslog": "^2.7.1",
"winston-transport": "^4.9.0", "winston-transport": "^4.9.0",
"ws": "^8.20.0", "ws": "^8.20.0",
"zigbee-herdsman": "10.0.5", "zigbee-herdsman": "10.0.7",
"zigbee-herdsman-converters": "26.27.0", "zigbee-herdsman-converters": "26.46.0",
"zigbee2mqtt-frontend": "0.9.21", "zigbee2mqtt-frontend": "0.9.21",
"zigbee2mqtt-windfront": "2.11.1" "zigbee2mqtt-windfront": "2.11.2"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^2.4.10", "@biomejs/biome": "^2.4.14",
"@types/finalhandler": "^1.2.3", "@types/finalhandler": "^1.2.3",
"@types/humanize-duration": "^3.27.4", "@types/humanize-duration": "^3.27.4",
"@types/js-yaml": "^4.0.9", "@types/js-yaml": "^4.0.9",
"@types/node": "^24.12.0", "@types/node": "^24.12.2",
"@types/object-assign-deep": "^0.4.3", "@types/object-assign-deep": "^0.4.3",
"@types/readable-stream": "4.0.23", "@types/readable-stream": "4.0.23",
"@types/serve-static": "^2.2.0", "@types/serve-static": "^2.2.0",
"@types/ws": "8.18.1", "@types/ws": "8.18.1",
"@vitest/coverage-v8": "^3.1.1", "@vitest/coverage-v8": "^3.1.1",
"tmp": "^0.2.5", "tmp": "^0.2.5",
"typescript": "^5.9.3", "typescript": "^6.0.3",
"vitest": "^3.1.1" "vitest": "^3.1.1"
}, },
"pnpm": { "pnpm": {
@@ -99,4 +99,4 @@
"optionalDependencies": { "optionalDependencies": {
"unix-dgram": "^2.0.7" "unix-dgram": "^2.0.7"
} }
} }
+112 -118
View File
@@ -5,15 +5,15 @@ settings:
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
overrides: overrides:
zigbee-herdsman: 10.0.5 zigbee-herdsman: 10.0.7
importers: importers:
.: .:
dependencies: dependencies:
ajv: ajv:
specifier: ^8.18.0 specifier: ^8.20.0
version: 8.18.0 version: 8.20.0
bind-decorator: bind-decorator:
specifier: ^1.0.11 specifier: ^1.0.11
version: 1.0.11 version: 1.0.11
@@ -21,8 +21,8 @@ importers:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.0.0 version: 3.0.0
express-static-gzip: express-static-gzip:
specifier: ^3.0.0 specifier: ^3.0.1
version: 3.0.0 version: 3.0.1
fast-deep-equal: fast-deep-equal:
specifier: ^3.1.3 specifier: ^3.1.3
version: 3.1.3 version: 3.1.3
@@ -72,21 +72,21 @@ importers:
specifier: ^8.20.0 specifier: ^8.20.0
version: 8.20.0 version: 8.20.0
zigbee-herdsman: zigbee-herdsman:
specifier: 10.0.5 specifier: 10.0.7
version: 10.0.5 version: 10.0.7
zigbee-herdsman-converters: zigbee-herdsman-converters:
specifier: 26.27.0 specifier: 26.46.0
version: 26.27.0 version: 26.46.0
zigbee2mqtt-frontend: zigbee2mqtt-frontend:
specifier: 0.9.21 specifier: 0.9.21
version: 0.9.21 version: 0.9.21
zigbee2mqtt-windfront: zigbee2mqtt-windfront:
specifier: 2.11.1 specifier: 2.11.2
version: 2.11.1 version: 2.11.2
devDependencies: devDependencies:
'@biomejs/biome': '@biomejs/biome':
specifier: ^2.4.10 specifier: ^2.4.14
version: 2.4.10 version: 2.4.14
'@types/finalhandler': '@types/finalhandler':
specifier: ^1.2.3 specifier: ^1.2.3
version: 1.2.4 version: 1.2.4
@@ -97,8 +97,8 @@ importers:
specifier: ^4.0.9 specifier: ^4.0.9
version: 4.0.9 version: 4.0.9
'@types/node': '@types/node':
specifier: ^24.12.0 specifier: ^24.12.2
version: 24.12.0 version: 24.12.2
'@types/object-assign-deep': '@types/object-assign-deep':
specifier: ^0.4.3 specifier: ^0.4.3
version: 0.4.3 version: 0.4.3
@@ -113,16 +113,16 @@ importers:
version: 8.18.1 version: 8.18.1
'@vitest/coverage-v8': '@vitest/coverage-v8':
specifier: ^3.1.1 specifier: ^3.1.1
version: 3.2.4(vitest@3.2.4(@types/node@24.12.0)) version: 3.2.4(vitest@3.2.4(@types/node@24.12.2))
tmp: tmp:
specifier: ^0.2.5 specifier: ^0.2.5
version: 0.2.5 version: 0.2.5
typescript: typescript:
specifier: ^5.9.3 specifier: ^6.0.3
version: 5.9.3 version: 6.0.3
vitest: vitest:
specifier: ^3.1.1 specifier: ^3.1.1
version: 3.2.4(@types/node@24.12.0) version: 3.2.4(@types/node@24.12.2)
optionalDependencies: optionalDependencies:
unix-dgram: unix-dgram:
specifier: ^2.0.7 specifier: ^2.0.7
@@ -159,55 +159,55 @@ packages:
resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==}
engines: {node: '>=18'} engines: {node: '>=18'}
'@biomejs/biome@2.4.10': '@biomejs/biome@2.4.14':
resolution: {integrity: sha512-xxA3AphFQ1geij4JTHXv4EeSTda1IFn22ye9LdyVPoJU19fNVl0uzfEuhsfQ4Yue/0FaLs2/ccVi4UDiE7R30w==} resolution: {integrity: sha512-TmAvxOEgrpLypzVGJ8FulIZnlyA9TxrO1hyqYrCz9r+bwma9xXxuLA5IuYnj55XQneFx460KjRbx6SWGLkg3bQ==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
hasBin: true hasBin: true
'@biomejs/cli-darwin-arm64@2.4.10': '@biomejs/cli-darwin-arm64@2.4.14':
resolution: {integrity: sha512-vuzzI1cWqDVzOMIkYyHbKqp+AkQq4K7k+UCXWpkYcY/HDn1UxdsbsfgtVpa40shem8Kax4TLDLlx8kMAecgqiw==} resolution: {integrity: sha512-XvgoE9XOawUOQPdmvs4J7wPhi/DLwSCGks3AlPJDmh34O0awRTqCED1HRcRDdpf1Zrp4us4MGOOdIxNpbqNF5Q==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@biomejs/cli-darwin-x64@2.4.10': '@biomejs/cli-darwin-x64@2.4.14':
resolution: {integrity: sha512-14fzASRo+BPotwp7nWULy2W5xeUyFnTaq1V13Etrrxkrih+ez/2QfgFm5Ehtf5vSjtgx/IJycMMpn5kPd5ZNaA==} resolution: {integrity: sha512-jE7hKBCFhOx3uUh+ZkWBfOHxAcILPfhFplNkuID/eZeSTLHzfZzoZxW8fbqY9xXRnPi7jGNAf1iPVR+0yWsM/Q==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@biomejs/cli-linux-arm64-musl@2.4.10': '@biomejs/cli-linux-arm64-musl@2.4.14':
resolution: {integrity: sha512-WrJY6UuiSD/Dh+nwK2qOTu8kdMDlLV3dLMmychIghHPAysWFq1/DGC1pVZx8POE3ZkzKR3PUUnVrtZfMfaJjyQ==} resolution: {integrity: sha512-/z+6gqAqqUQTHazwStxSXKHg9b8UvqBmDFRp+c4wYbq2KXhELQDon9EoC9RpmQ8JWkqQx/lIUy/cs+MhzDZp6A==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@biomejs/cli-linux-arm64@2.4.10': '@biomejs/cli-linux-arm64@2.4.14':
resolution: {integrity: sha512-7MH1CMW5uuxQ/s7FLST63qF8B3Hgu2HRdZ7tA1X1+mk+St4JOuIrqdhIBnnyqeyWJNI+Bww7Es5QZ0wIc1Cmkw==} resolution: {integrity: sha512-2TELhZnW5RSLL063l9rc5xLpA0ZIw0Ccwy/0q384rvNAgFw3yI76bd59547yxowdQr5MNPET/xDLrLuvgSeeWQ==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@biomejs/cli-linux-x64-musl@2.4.10': '@biomejs/cli-linux-x64-musl@2.4.14':
resolution: {integrity: sha512-kDTi3pI6PBN6CiczsWYOyP2zk0IJI08EWEQyDMQWW221rPaaEz6FvjLhnU07KMzLv8q3qSuoB93ua6inSQ55Tw==} resolution: {integrity: sha512-R6BWgJdQOwW9ulJatuTVrQkjnODjqHZkKNOqb1sz++3Noe5LYd0i3PchnOBUCYAPHoPWHhjJqbdZlHEu0hpjdA==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@biomejs/cli-linux-x64@2.4.10': '@biomejs/cli-linux-x64@2.4.14':
resolution: {integrity: sha512-tZLvEEi2u9Xu1zAqRjTcpIDGVtldigVvzug2fTuPG0ME/g8/mXpRPcNgLB22bGn6FvLJpHHnqLnwliOu8xjYrg==} resolution: {integrity: sha512-zHrlQZDBDUz4OLAraYpWKcnLS6HOewBFWYOzY91d1ZjdqZwibOyb6BEu6WuWLugyo0P3riCmsbV9UqV1cSXwQg==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@biomejs/cli-win32-arm64@2.4.10': '@biomejs/cli-win32-arm64@2.4.14':
resolution: {integrity: sha512-umwQU6qPzH+ISTf/eHyJ/QoQnJs3V9Vpjz2OjZXe9MVBZ7prgGafMy7yYeRGnlmDAn87AKTF3Q6weLoMGpeqdQ==} resolution: {integrity: sha512-M3EH5hqOI/F/FUA2u4xcLoUgmxd218mvuj/6JL7Hv2toQvr2/AdOvKSpGkoRuWFCtQPVa+ZqkEV3Q5xBA9+XSA==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@biomejs/cli-win32-x64@2.4.10': '@biomejs/cli-win32-x64@2.4.14':
resolution: {integrity: sha512-aW/JU5GuyH4uxMrNYpoC2kjaHlyJGLgIa3XkhPEZI0uKhZhJZU8BuEyJmvgzSPQNGozBwWjC972RaNdcJ9KyJg==} resolution: {integrity: sha512-WL0EG5qE+EAKomGXbf2g6VnSKJhTL3tXC0QRzWRwA5VpjxNYa6H4P7ZWfymbGE4IhZZQi1KXQ2R0YjwInmz2fA==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
@@ -553,8 +553,8 @@ packages:
'@types/js-yaml@4.0.9': '@types/js-yaml@4.0.9':
resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==}
'@types/node@24.12.0': '@types/node@24.12.2':
resolution: {integrity: sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==} resolution: {integrity: sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==}
'@types/object-assign-deep@0.4.3': '@types/object-assign-deep@0.4.3':
resolution: {integrity: sha512-d9Gxaj5j1hzrxJ61EFEg13B4g4FgrT/DYtcDWFXPehR8DF2SUZbVMFtZIs8exkVRiqrqBpdTc/lUUZjncsPpMw==} resolution: {integrity: sha512-d9Gxaj5j1hzrxJ61EFEg13B4g4FgrT/DYtcDWFXPehR8DF2SUZbVMFtZIs8exkVRiqrqBpdTc/lUUZjncsPpMw==}
@@ -613,8 +613,8 @@ packages:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
engines: {node: '>=6.5'} engines: {node: '>=6.5'}
ajv@8.18.0: ajv@8.20.0:
resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==}
ansi-regex@5.0.1: ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
@@ -824,8 +824,8 @@ packages:
resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
express-static-gzip@3.0.0: express-static-gzip@3.0.1:
resolution: {integrity: sha512-36O10S0asHl3QojOBQQ0ZjXNtElmhgPS6erSUCCZymXkB/CK1mnGqOj4BTJN+FYRDIzVFnzo3wLFCZJvAk6rQQ==} resolution: {integrity: sha512-LMeU/3YjFlFUa4vrPX+RoMMRW5mIpF4Iysgs6gX7A59WCY4BzyF3O28mBr4eMlWuW4DU9wVAVuVcfx29ln1N6g==}
fast-deep-equal@3.1.3: fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
@@ -894,8 +894,8 @@ packages:
html-escaper@2.0.2: html-escaper@2.0.2:
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
http-errors@2.0.0: http-errors@2.0.1:
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
humanize-duration@3.33.2: humanize-duration@3.33.2:
@@ -1008,9 +1008,9 @@ packages:
resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
mime-types@3.0.1: mime-types@3.0.2:
resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==}
engines: {node: '>= 0.6'} engines: {node: '>=18'}
minimatch@10.2.0: minimatch@10.2.0:
resolution: {integrity: sha512-ugkC31VaVg9cF0DFVoADH12k6061zNZkZON+aX8AWsR9GhPcErkcMBceb6znR8wLERM2AkkOxy2nWRLpT9Jq5w==} resolution: {integrity: sha512-ugkC31VaVg9cF0DFVoADH12k6061zNZkZON+aX8AWsR9GhPcErkcMBceb6znR8wLERM2AkkOxy2nWRLpT9Jq5w==}
@@ -1169,12 +1169,12 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
send@1.2.0: send@1.2.1:
resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==}
engines: {node: '>= 18'} engines: {node: '>= 18'}
serve-static@2.2.0: serve-static@2.2.1:
resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==}
engines: {node: '>= 18'} engines: {node: '>= 18'}
setimmediate@1.0.5: setimmediate@1.0.5:
@@ -1230,10 +1230,6 @@ packages:
stackback@0.0.2: stackback@0.0.2:
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
statuses@2.0.1:
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
engines: {node: '>= 0.8'}
statuses@2.0.2: statuses@2.0.2:
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
@@ -1324,8 +1320,8 @@ packages:
typedarray@0.0.6: typedarray@0.0.6:
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
typescript@5.9.3: typescript@6.0.3:
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
engines: {node: '>=14.17'} engines: {node: '>=14.17'}
hasBin: true hasBin: true
@@ -1472,12 +1468,12 @@ packages:
utf-8-validate: utf-8-validate:
optional: true optional: true
zigbee-herdsman-converters@26.27.0: zigbee-herdsman-converters@26.46.0:
resolution: {integrity: sha512-l0Tc/TBRML5lO298NRxr2CtKcQ5YxA96FINJuifyGGG62mANSa+UmAcvl0P/fnPo8r7q8wdCYxXrb/7lYILZKA==} resolution: {integrity: sha512-RFRQQoxGrz8si/wsFOPMdFXF1ppO0mQxU9cyRvzT98HV4bL3dnUKEWIdo6caCllLn6izA5kdob9vtxXqruU/SQ==}
engines: {node: '>=20.15.0'} engines: {node: '>=20.15.0'}
zigbee-herdsman@10.0.5: zigbee-herdsman@10.0.7:
resolution: {integrity: sha512-zCXpvzTxQ9pkuYpMV6PVb6gX8AUGpl99ut80Q96bWCqXKHNnkbn1wwrpdaXuvGhoWVB8En9hp8ZnipbF9sODEw==} resolution: {integrity: sha512-QqKJaJryWf7IUuVNV6Dc1/qNPrs2dJANPJhRTG74nZBAsyIDn/vSyCP7g19fuEM7Cphr/0ZgCJZkHP2wKvNPMw==}
zigbee-on-host@0.2.4: zigbee-on-host@0.2.4:
resolution: {integrity: sha512-NIG6CWp+Yfn7PjqEIRvenHqpwT1U7rSkyimnFOUIFpnmxQOJKrmcwWDfn6WjbU/YIYYWSQPlj+g13icu1xwlyg==} resolution: {integrity: sha512-NIG6CWp+Yfn7PjqEIRvenHqpwT1U7rSkyimnFOUIFpnmxQOJKrmcwWDfn6WjbU/YIYYWSQPlj+g13icu1xwlyg==}
@@ -1487,8 +1483,8 @@ packages:
resolution: {integrity: sha512-ClHYlG7g0v/tnIsD6kycNCi1ZEKIbQuU7lGJR3WqYcDmuqEMVcHDGbRiRwYRQcZGMxkqCf7guxNsXiKnC10+kg==} resolution: {integrity: sha512-ClHYlG7g0v/tnIsD6kycNCi1ZEKIbQuU7lGJR3WqYcDmuqEMVcHDGbRiRwYRQcZGMxkqCf7guxNsXiKnC10+kg==}
engines: {node: '>=20.11'} engines: {node: '>=20.11'}
zigbee2mqtt-windfront@2.11.1: zigbee2mqtt-windfront@2.11.2:
resolution: {integrity: sha512-2xM01JnuCWZlb1xYkIBVFAGJ6mkuAq/DRi8kfmB8KlA77LzHuX+f85pdgDotF9WGWb4+TqxmnpifYri1Nc3K2A==} resolution: {integrity: sha512-NzuPB8VHZsuumb3euxX73fXtM+neLCgabhBUM1UoVHubF//w9afe6Wuws2e1W59yOSd03lAn9/Bkmt6sgDgjwQ==}
engines: {node: '>=22.12.0'} engines: {node: '>=22.12.0'}
snapshots: snapshots:
@@ -1515,39 +1511,39 @@ snapshots:
'@bcoe/v8-coverage@1.0.2': {} '@bcoe/v8-coverage@1.0.2': {}
'@biomejs/biome@2.4.10': '@biomejs/biome@2.4.14':
optionalDependencies: optionalDependencies:
'@biomejs/cli-darwin-arm64': 2.4.10 '@biomejs/cli-darwin-arm64': 2.4.14
'@biomejs/cli-darwin-x64': 2.4.10 '@biomejs/cli-darwin-x64': 2.4.14
'@biomejs/cli-linux-arm64': 2.4.10 '@biomejs/cli-linux-arm64': 2.4.14
'@biomejs/cli-linux-arm64-musl': 2.4.10 '@biomejs/cli-linux-arm64-musl': 2.4.14
'@biomejs/cli-linux-x64': 2.4.10 '@biomejs/cli-linux-x64': 2.4.14
'@biomejs/cli-linux-x64-musl': 2.4.10 '@biomejs/cli-linux-x64-musl': 2.4.14
'@biomejs/cli-win32-arm64': 2.4.10 '@biomejs/cli-win32-arm64': 2.4.14
'@biomejs/cli-win32-x64': 2.4.10 '@biomejs/cli-win32-x64': 2.4.14
'@biomejs/cli-darwin-arm64@2.4.10': '@biomejs/cli-darwin-arm64@2.4.14':
optional: true optional: true
'@biomejs/cli-darwin-x64@2.4.10': '@biomejs/cli-darwin-x64@2.4.14':
optional: true optional: true
'@biomejs/cli-linux-arm64-musl@2.4.10': '@biomejs/cli-linux-arm64-musl@2.4.14':
optional: true optional: true
'@biomejs/cli-linux-arm64@2.4.10': '@biomejs/cli-linux-arm64@2.4.14':
optional: true optional: true
'@biomejs/cli-linux-x64-musl@2.4.10': '@biomejs/cli-linux-x64-musl@2.4.14':
optional: true optional: true
'@biomejs/cli-linux-x64@2.4.10': '@biomejs/cli-linux-x64@2.4.14':
optional: true optional: true
'@biomejs/cli-win32-arm64@2.4.10': '@biomejs/cli-win32-arm64@2.4.14':
optional: true optional: true
'@biomejs/cli-win32-x64@2.4.10': '@biomejs/cli-win32-x64@2.4.14':
optional: true optional: true
'@colors/colors@1.6.0': {} '@colors/colors@1.6.0': {}
@@ -1770,7 +1766,7 @@ snapshots:
'@types/finalhandler@1.2.4': '@types/finalhandler@1.2.4':
dependencies: dependencies:
'@types/node': 24.12.0 '@types/node': 24.12.2
'@types/http-errors@2.0.5': {} '@types/http-errors@2.0.5': {}
@@ -1778,7 +1774,7 @@ snapshots:
'@types/js-yaml@4.0.9': {} '@types/js-yaml@4.0.9': {}
'@types/node@24.12.0': '@types/node@24.12.2':
dependencies: dependencies:
undici-types: 7.16.0 undici-types: 7.16.0
@@ -1786,20 +1782,20 @@ snapshots:
'@types/readable-stream@4.0.23': '@types/readable-stream@4.0.23':
dependencies: dependencies:
'@types/node': 24.12.0 '@types/node': 24.12.2
'@types/serve-static@2.2.0': '@types/serve-static@2.2.0':
dependencies: dependencies:
'@types/http-errors': 2.0.5 '@types/http-errors': 2.0.5
'@types/node': 24.12.0 '@types/node': 24.12.2
'@types/triple-beam@1.3.5': {} '@types/triple-beam@1.3.5': {}
'@types/ws@8.18.1': '@types/ws@8.18.1':
dependencies: dependencies:
'@types/node': 24.12.0 '@types/node': 24.12.2
'@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/node@24.12.0))': '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/node@24.12.2))':
dependencies: dependencies:
'@ampproject/remapping': 2.3.0 '@ampproject/remapping': 2.3.0
'@bcoe/v8-coverage': 1.0.2 '@bcoe/v8-coverage': 1.0.2
@@ -1814,7 +1810,7 @@ snapshots:
std-env: 3.9.0 std-env: 3.9.0
test-exclude: 7.0.1 test-exclude: 7.0.1
tinyrainbow: 2.0.0 tinyrainbow: 2.0.0
vitest: 3.2.4(@types/node@24.12.0) vitest: 3.2.4(@types/node@24.12.2)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -1826,13 +1822,13 @@ snapshots:
chai: 5.2.0 chai: 5.2.0
tinyrainbow: 2.0.0 tinyrainbow: 2.0.0
'@vitest/mocker@3.2.4(vite@6.3.5(@types/node@24.12.0))': '@vitest/mocker@3.2.4(vite@6.3.5(@types/node@24.12.2))':
dependencies: dependencies:
'@vitest/spy': 3.2.4 '@vitest/spy': 3.2.4
estree-walker: 3.0.3 estree-walker: 3.0.3
magic-string: 0.30.17 magic-string: 0.30.17
optionalDependencies: optionalDependencies:
vite: 6.3.5(@types/node@24.12.0) vite: 6.3.5(@types/node@24.12.2)
'@vitest/pretty-format@3.2.4': '@vitest/pretty-format@3.2.4':
dependencies: dependencies:
@@ -1864,7 +1860,7 @@ snapshots:
dependencies: dependencies:
event-target-shim: 5.0.1 event-target-shim: 5.0.1
ajv@8.18.0: ajv@8.20.0:
dependencies: dependencies:
fast-deep-equal: 3.1.3 fast-deep-equal: 3.1.3
fast-uri: 3.1.0 fast-uri: 3.1.0
@@ -2070,11 +2066,11 @@ snapshots:
expect-type@1.2.1: {} expect-type@1.2.1: {}
express-static-gzip@3.0.0: express-static-gzip@3.0.1:
dependencies: dependencies:
mime-types: 3.0.1 mime-types: 3.0.2
parseurl: 1.3.3 parseurl: 1.3.3
serve-static: 2.2.0 serve-static: 2.2.1
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -2142,12 +2138,12 @@ snapshots:
html-escaper@2.0.2: {} html-escaper@2.0.2: {}
http-errors@2.0.0: http-errors@2.0.1:
dependencies: dependencies:
depd: 2.0.0 depd: 2.0.0
inherits: 2.0.4 inherits: 2.0.4
setprototypeof: 1.2.0 setprototypeof: 1.2.0
statuses: 2.0.1 statuses: 2.0.2
toidentifier: 1.0.1 toidentifier: 1.0.1
humanize-duration@3.33.2: {} humanize-duration@3.33.2: {}
@@ -2259,7 +2255,7 @@ snapshots:
mime-db@1.54.0: {} mime-db@1.54.0: {}
mime-types@3.0.1: mime-types@3.0.2:
dependencies: dependencies:
mime-db: 1.54.0 mime-db: 1.54.0
@@ -2446,15 +2442,15 @@ snapshots:
semver@7.7.4: {} semver@7.7.4: {}
send@1.2.0: send@1.2.1:
dependencies: dependencies:
debug: 4.4.3 debug: 4.4.3
encodeurl: 2.0.0 encodeurl: 2.0.0
escape-html: 1.0.3 escape-html: 1.0.3
etag: 1.8.1 etag: 1.8.1
fresh: 2.0.0 fresh: 2.0.0
http-errors: 2.0.0 http-errors: 2.0.1
mime-types: 3.0.1 mime-types: 3.0.2
ms: 2.1.3 ms: 2.1.3
on-finished: 2.4.1 on-finished: 2.4.1
range-parser: 1.2.1 range-parser: 1.2.1
@@ -2462,12 +2458,12 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
serve-static@2.2.0: serve-static@2.2.1:
dependencies: dependencies:
encodeurl: 2.0.0 encodeurl: 2.0.0
escape-html: 1.0.3 escape-html: 1.0.3
parseurl: 1.3.3 parseurl: 1.3.3
send: 1.2.0 send: 1.2.1
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -2509,8 +2505,6 @@ snapshots:
stackback@0.0.2: {} stackback@0.0.2: {}
statuses@2.0.1: {}
statuses@2.0.2: {} statuses@2.0.2: {}
std-env@3.9.0: {} std-env@3.9.0: {}
@@ -2588,7 +2582,7 @@ snapshots:
typedarray@0.0.6: {} typedarray@0.0.6: {}
typescript@5.9.3: {} typescript@6.0.3: {}
undici-types@7.16.0: {} undici-types@7.16.0: {}
@@ -2606,13 +2600,13 @@ snapshots:
util-deprecate@1.0.2: {} util-deprecate@1.0.2: {}
vite-node@3.2.4(@types/node@24.12.0): vite-node@3.2.4(@types/node@24.12.2):
dependencies: dependencies:
cac: 6.7.14 cac: 6.7.14
debug: 4.4.3 debug: 4.4.3
es-module-lexer: 1.7.0 es-module-lexer: 1.7.0
pathe: 2.0.3 pathe: 2.0.3
vite: 6.3.5(@types/node@24.12.0) vite: 6.3.5(@types/node@24.12.2)
transitivePeerDependencies: transitivePeerDependencies:
- '@types/node' - '@types/node'
- jiti - jiti
@@ -2627,7 +2621,7 @@ snapshots:
- tsx - tsx
- yaml - yaml
vite@6.3.5(@types/node@24.12.0): vite@6.3.5(@types/node@24.12.2):
dependencies: dependencies:
esbuild: 0.25.5 esbuild: 0.25.5
fdir: 6.4.6(picomatch@4.0.2) fdir: 6.4.6(picomatch@4.0.2)
@@ -2636,14 +2630,14 @@ snapshots:
rollup: 4.44.0 rollup: 4.44.0
tinyglobby: 0.2.14 tinyglobby: 0.2.14
optionalDependencies: optionalDependencies:
'@types/node': 24.12.0 '@types/node': 24.12.2
fsevents: 2.3.3 fsevents: 2.3.3
vitest@3.2.4(@types/node@24.12.0): vitest@3.2.4(@types/node@24.12.2):
dependencies: dependencies:
'@types/chai': 5.2.2 '@types/chai': 5.2.2
'@vitest/expect': 3.2.4 '@vitest/expect': 3.2.4
'@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@24.12.0)) '@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@24.12.2))
'@vitest/pretty-format': 3.2.4 '@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4 '@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4 '@vitest/snapshot': 3.2.4
@@ -2661,11 +2655,11 @@ snapshots:
tinyglobby: 0.2.14 tinyglobby: 0.2.14
tinypool: 1.1.1 tinypool: 1.1.1
tinyrainbow: 2.0.0 tinyrainbow: 2.0.0
vite: 6.3.5(@types/node@24.12.0) vite: 6.3.5(@types/node@24.12.2)
vite-node: 3.2.4(@types/node@24.12.0) vite-node: 3.2.4(@types/node@24.12.2)
why-is-node-running: 2.3.0 why-is-node-running: 2.3.0
optionalDependencies: optionalDependencies:
'@types/node': 24.12.0 '@types/node': 24.12.2
transitivePeerDependencies: transitivePeerDependencies:
- jiti - jiti
- less - less
@@ -2759,15 +2753,15 @@ snapshots:
ws@8.20.0: {} ws@8.20.0: {}
zigbee-herdsman-converters@26.27.0: zigbee-herdsman-converters@26.46.0:
dependencies: dependencies:
iconv-lite: 0.7.2 iconv-lite: 0.7.2
semver: 7.7.4 semver: 7.7.4
zigbee-herdsman: 10.0.5 zigbee-herdsman: 10.0.7
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
zigbee-herdsman@10.0.5: zigbee-herdsman@10.0.7:
dependencies: dependencies:
'@date-fns/tz': 1.4.1 '@date-fns/tz': 1.4.1
'@serialport/bindings-cpp': 13.0.1 '@serialport/bindings-cpp': 13.0.1
@@ -2785,4 +2779,4 @@ snapshots:
zigbee2mqtt-frontend@0.9.21: {} zigbee2mqtt-frontend@0.9.21: {}
zigbee2mqtt-windfront@2.11.1: {} zigbee2mqtt-windfront@2.11.2: {}
+61 -10
View File
@@ -735,7 +735,7 @@ describe("Extension: Bridge", () => {
values: ["off", "on", "toggle", "previous"], values: ["off", "on", "toggle", "previous"],
}, },
{ {
access: 2, access: 3,
label: "Effect", label: "Effect",
name: "effect", name: "effect",
property: "effect", property: "effect",
@@ -748,11 +748,41 @@ describe("Extension: Bridge", () => {
"candle", "candle",
"fireplace", "fireplace",
"colorloop", "colorloop",
"sunset",
"sunrise",
"sparkle",
"opal",
"glisten",
"underwater",
"cosmos",
"sunbeam",
"enchant",
"none",
"finish_effect", "finish_effect",
"stop_effect", "stop_effect",
"stop_hue_effect", "stop_hue_effect",
], ],
}, },
{
access: 3,
description: "Animation speed for the active effect (0=slowest, 1=fastest)",
label: "Effect speed",
name: "effect_speed",
property: "effect_speed",
type: "numeric",
value_max: 1,
value_min: 0,
value_step: 0.01,
},
{
access: 2,
description:
'Set the base color of the active effect without stopping it (hex e.g. #FF4400, or JSON {"x":0.6,"y":0.3})',
label: "Effect color",
name: "effect_color",
property: "effect_color",
type: "text",
},
{ {
access: 1, access: 1,
category: "diagnostic", category: "diagnostic",
@@ -768,6 +798,27 @@ describe("Extension: Bridge", () => {
], ],
model: "7146060PH", model: "7146060PH",
options: [ options: [
{
access: 2,
description:
"Control this light using a Philips-specific protocol instead of standard Zigbee commands. When enabled, on/off, brightness, color, and color temperature are combined into single atomic commands. This is required to use the Effect color update mode. When disabled (default), standard Zigbee commands are used, which preserves the usual behavior, including simulating on/off transitions.",
label: "Hue native control",
name: "hue_native_control",
property: "hue_native_control",
type: "binary",
value_off: false,
value_on: true,
},
{
access: 2,
description:
"Controls what happens when color is changed while an effect is active (requires Hue native control). 'stop' (default): color change stops the effect (Hue app behavior). 'update': color change re-sends the effect with the new color.",
label: "Effect color mode",
name: "effect_color_mode",
property: "effect_color_mode",
type: "enum",
values: ["stop", "update"],
},
{ {
access: 2, access: 2,
description: description:
@@ -781,21 +832,21 @@ describe("Extension: Bridge", () => {
}, },
{ {
access: 2, access: 2,
description: description: "State actions will also be published as 'action' when true (default false).",
"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: "State action",
label: "Color sync", name: "state_action",
name: "color_sync", property: "state_action",
property: "color_sync",
type: "binary", type: "binary",
value_off: false, value_off: false,
value_on: true, value_on: true,
}, },
{ {
access: 2, access: 2,
description: "State actions will also be published as 'action' when true (default false).", description:
label: "State action", "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).",
name: "state_action", label: "Color sync",
property: "state_action", name: "color_sync",
property: "color_sync",
type: "binary", type: "binary",
value_off: false, value_off: false,
value_on: true, value_on: true,
+6
View File
@@ -245,6 +245,8 @@ describe("Extension: Frontend", () => {
stringify({ stringify({
state: "ON", state: "ON",
effect: null, effect: null,
effect_color: null,
effect_speed: null,
power_on_behavior: null, power_on_behavior: null,
linkquality: 20, linkquality: 20,
update: {state: null, installed_version: -1, latest_version: -1}, update: {state: null, installed_version: -1, latest_version: -1},
@@ -269,6 +271,8 @@ describe("Extension: Frontend", () => {
state: "ON", state: "ON",
power_on_behavior: null, power_on_behavior: null,
effect: null, effect: null,
effect_color: null,
effect_speed: null,
linkquality: 20, linkquality: 20,
update: {state: null, installed_version: -1, latest_version: -1}, update: {state: null, installed_version: -1, latest_version: -1},
}, },
@@ -296,6 +300,8 @@ describe("Extension: Frontend", () => {
brightness: 90, brightness: 90,
power_on_behavior: null, power_on_behavior: null,
effect: null, effect: null,
effect_color: null,
effect_speed: null,
linkquality: 20, linkquality: 20,
update: {state: null, installed_version: -1, latest_version: -1}, update: {state: null, installed_version: -1, latest_version: -1},
}, },
+53 -1
View File
@@ -103,6 +103,7 @@ describe("Extension: HomeAssistant", () => {
definition: d, definition: d,
isDevice: (): boolean => true, isDevice: (): boolean => true,
isGroup: (): boolean => false, isGroup: (): boolean => false,
endpoint: () => undefined,
options: {}, options: {},
exposes: (): unknown[] => exposes, exposes: (): unknown[] => exposes,
zh: {endpoints: []}, zh: {endpoints: []},
@@ -124,7 +125,7 @@ describe("Extension: HomeAssistant", () => {
} }
} }
expect(duplicated).toHaveLength(0); expect(duplicated).toStrictEqual([]);
}); });
it("Should discover devices and groups", async () => { it("Should discover devices and groups", async () => {
@@ -163,6 +164,16 @@ describe("Extension: HomeAssistant", () => {
"candle", "candle",
"fireplace", "fireplace",
"colorloop", "colorloop",
"sunset",
"sunrise",
"sparkle",
"opal",
"glisten",
"underwater",
"cosmos",
"sunbeam",
"enchant",
"none",
"finish_effect", "finish_effect",
"stop_effect", "stop_effect",
"stop_hue_effect", "stop_hue_effect",
@@ -170,6 +181,7 @@ describe("Extension: HomeAssistant", () => {
object_id: "ha_discovery_group", object_id: "ha_discovery_group",
default_entity_id: "light.ha_discovery_group", default_entity_id: "light.ha_discovery_group",
unique_id: "9_light_zigbee2mqtt", unique_id: "9_light_zigbee2mqtt",
group: ["0x000b57fffec6a5b4_light_zigbee2mqtt", "0x000b57fffec6a5b7_light_zigbee2mqtt"],
origin: origin, origin: origin,
}; };
@@ -225,6 +237,7 @@ describe("Extension: HomeAssistant", () => {
object_id: "ha_discovery_group", object_id: "ha_discovery_group",
default_entity_id: "switch.ha_discovery_group", default_entity_id: "switch.ha_discovery_group",
unique_id: "9_switch_zigbee2mqtt", unique_id: "9_switch_zigbee2mqtt",
group: ["0x0017880104e45542_switch_right_zigbee2mqtt"],
origin: origin, origin: origin,
value_template: "{{ value_json.state }}", value_template: "{{ value_json.state }}",
}; };
@@ -1558,6 +1571,8 @@ describe("Extension: HomeAssistant", () => {
color: {hue: 0, saturation: 100, h: 0, s: 100}, color: {hue: 0, saturation: 100, h: 0, s: 100},
color_mode: "hs", color_mode: "hs",
effect: null, effect: null,
effect_color: null,
effect_speed: null,
linkquality: null, linkquality: null,
state: null, state: null,
power_on_behavior: null, power_on_behavior: null,
@@ -1581,6 +1596,8 @@ describe("Extension: HomeAssistant", () => {
color: {x: 0.4576, y: 0.41}, color: {x: 0.4576, y: 0.41},
color_mode: "xy", color_mode: "xy",
effect: null, effect: null,
effect_color: null,
effect_speed: null,
linkquality: null, linkquality: null,
state: null, state: null,
power_on_behavior: null, power_on_behavior: null,
@@ -1603,6 +1620,8 @@ describe("Extension: HomeAssistant", () => {
stringify({ stringify({
linkquality: null, linkquality: null,
effect: null, effect: null,
effect_color: null,
effect_speed: null,
state: "ON", state: "ON",
power_on_behavior: null, power_on_behavior: null,
update: {state: null, installed_version: -1, latest_version: -1}, update: {state: null, installed_version: -1, latest_version: -1},
@@ -1965,6 +1984,16 @@ describe("Extension: HomeAssistant", () => {
"candle", "candle",
"fireplace", "fireplace",
"colorloop", "colorloop",
"sunset",
"sunrise",
"sparkle",
"opal",
"glisten",
"underwater",
"cosmos",
"sunbeam",
"enchant",
"none",
"finish_effect", "finish_effect",
"stop_effect", "stop_effect",
"stop_hue_effect", "stop_hue_effect",
@@ -1972,6 +2001,7 @@ describe("Extension: HomeAssistant", () => {
object_id: "ha_discovery_group_new", object_id: "ha_discovery_group_new",
default_entity_id: "light.ha_discovery_group_new", default_entity_id: "light.ha_discovery_group_new",
unique_id: "9_light_zigbee2mqtt", unique_id: "9_light_zigbee2mqtt",
group: ["0x000b57fffec6a5b4_light_zigbee2mqtt", "0x000b57fffec6a5b7_light_zigbee2mqtt"],
origin: origin, origin: origin,
}; };
@@ -2434,6 +2464,16 @@ describe("Extension: HomeAssistant", () => {
"candle", "candle",
"fireplace", "fireplace",
"colorloop", "colorloop",
"sunset",
"sunrise",
"sparkle",
"opal",
"glisten",
"underwater",
"cosmos",
"sunbeam",
"enchant",
"none",
"finish_effect", "finish_effect",
"stop_effect", "stop_effect",
"stop_hue_effect", "stop_hue_effect",
@@ -2441,6 +2481,7 @@ describe("Extension: HomeAssistant", () => {
object_id: "ha_discovery_group", object_id: "ha_discovery_group",
default_entity_id: "light.ha_discovery_group", default_entity_id: "light.ha_discovery_group",
unique_id: "9_light_zigbee2mqtt", unique_id: "9_light_zigbee2mqtt",
group: ["0x000b57fffec6a5b4_light_zigbee2mqtt", "0x000b57fffec6a5b7_light_zigbee2mqtt"],
origin: origin, origin: origin,
}; };
@@ -2479,6 +2520,16 @@ describe("Extension: HomeAssistant", () => {
"candle", "candle",
"fireplace", "fireplace",
"colorloop", "colorloop",
"sunset",
"sunrise",
"sparkle",
"opal",
"glisten",
"underwater",
"cosmos",
"sunbeam",
"enchant",
"none",
"finish_effect", "finish_effect",
"stop_effect", "stop_effect",
"stop_hue_effect", "stop_hue_effect",
@@ -2486,6 +2537,7 @@ describe("Extension: HomeAssistant", () => {
object_id: "ha_discovery_group", object_id: "ha_discovery_group",
default_entity_id: "light.ha_discovery_group", default_entity_id: "light.ha_discovery_group",
unique_id: "9_light_zigbee2mqtt", unique_id: "9_light_zigbee2mqtt",
group: ["0x000b57fffec6a5b4_light_zigbee2mqtt", "0x000b57fffec6a5b7_light_zigbee2mqtt"],
origin: origin, origin: origin,
}; };
+3 -3
View File
@@ -286,7 +286,7 @@ describe("Extension: NetworkMap", () => {
description: "Hue Go", description: "Hue Go",
model: "7146060PH", model: "7146060PH",
supports: supports:
"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality", "light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, effect_speed, effect_color, linkquality",
vendor: "Philips", vendor: "Philips",
}, },
failed: [], failed: [],
@@ -616,7 +616,7 @@ describe("Extension: NetworkMap", () => {
description: "Hue Go", description: "Hue Go",
model: "7146060PH", model: "7146060PH",
supports: supports:
"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality", "light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, effect_speed, effect_color, linkquality",
vendor: "Philips", vendor: "Philips",
}, },
failed: [], failed: [],
@@ -785,7 +785,7 @@ describe("Extension: NetworkMap", () => {
description: "Hue Go", description: "Hue Go",
model: "7146060PH", model: "7146060PH",
supports: supports:
"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality", "light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, effect_speed, effect_color, linkquality",
vendor: "Philips", vendor: "Philips",
}, },
failed: [], failed: [],
-1
View File
@@ -11,7 +11,6 @@
"declaration": true, "declaration": true,
"declarationMap": true, "declarationMap": true,
"outDir": "dist", "outDir": "dist",
"baseUrl": ".",
"rootDir": "lib", "rootDir": "lib",
"inlineSourceMap": true, "inlineSourceMap": true,
"resolveJsonModule": true, "resolveJsonModule": true,