Compare commits

..
Author SHA1 Message Date
Mihonarium ac8730a624 test: update Philips light fixtures for hue_native_control
Updates z2m test fixtures to match the new exposes and options added by
the Hue native control changes in zigbee-herdsman-converters patch-1:
- effect enum gains 9 new values and switches to STATE_SET access
- new effect_speed and effect_color exposes
- new hue_native_control and effect_color_mode options

Affects only Philips LLC020 (bulb_color / bulb_color_2) fixtures and the
ha_discovery_group that contains it.
2026-04-26 21:58:58 +02:00
26 changed files with 346 additions and 625 deletions
+2 -2
View File
@@ -31,7 +31,7 @@ jobs:
- uses: actions/checkout@v6
if: ((github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push') == false
- uses: pnpm/action-setup@v6
- uses: pnpm/action-setup@v5
- uses: actions/setup-node@v6
with:
@@ -222,7 +222,7 @@ jobs:
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v6
- uses: pnpm/action-setup@v5
- uses: actions/setup-node@v6
with:
+1 -1
View File
@@ -12,4 +12,4 @@ jobs:
- name: 'Checkout repository'
uses: actions/checkout@v6
- name: 'Dependency review'
uses: actions/dependency-review-action@v5
uses: actions/dependency-review-action@v4
+2 -2
View File
@@ -16,7 +16,7 @@ jobs:
release_created: ${{ steps.release.outputs.release_created }}
version: '${{steps.release.outputs.major}}.${{steps.release.outputs.minor}}.${{steps.release.outputs.patch}}'
steps:
- uses: pnpm/action-setup@v6
- uses: pnpm/action-setup@v5
with:
version: 9
@@ -24,7 +24,7 @@ jobs:
with:
node-version: 24
- uses: googleapis/release-please-action@v5
- uses: googleapis/release-please-action@v4
id: release
with:
target-branch: dev
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
with:
ref: dev
token: ${{ secrets.GH_TOKEN }}
- uses: pnpm/action-setup@v6
- uses: pnpm/action-setup@v5
- uses: actions/setup-node@v6
with:
node-version: 24
+1 -1
View File
@@ -1,3 +1,3 @@
{
".": "2.12.0"
".": "2.9.2"
}
-110
View File
@@ -1,115 +1,5 @@
# Changelog
## [2.12.0](https://github.com/Koenkk/zigbee2mqtt/compare/2.11.0...2.12.0) (2026-06-09)
### Features
* Add ability to abort running OTA ([#32022](https://github.com/Koenkk/zigbee2mqtt/issues/32022)) ([de58711](https://github.com/Koenkk/zigbee2mqtt/commit/de58711e11a5ca07c5b55a0c17a1d9f3f452c110))
### Bug Fixes
* **ignore:** bump the minor-patch group across 1 directory with 3 updates ([#32236](https://github.com/Koenkk/zigbee2mqtt/issues/32236)) ([41f6886](https://github.com/Koenkk/zigbee2mqtt/commit/41f688659119012a9a84063bd67c2769a1b1892c))
* **ignore:** bump the minor-patch group with 2 updates ([#32183](https://github.com/Koenkk/zigbee2mqtt/issues/32183)) ([ef8dcb4](https://github.com/Koenkk/zigbee2mqtt/commit/ef8dcb4dc2800defd97fda7523297eab8d2566dc))
* **ignore:** missing type for new OTA endpoint ([#32195](https://github.com/Koenkk/zigbee2mqtt/issues/32195)) ([6c2c874](https://github.com/Koenkk/zigbee2mqtt/commit/6c2c874641ada9cae1c9bdd963a3201cb1cf0fef))
* **ignore:** update zigbee-herdsman to 10.2.0 ([#32179](https://github.com/Koenkk/zigbee2mqtt/issues/32179)) ([f9e1bac](https://github.com/Koenkk/zigbee2mqtt/commit/f9e1bac588e65b223e75f4a2b2e9d528933fdbd8))
* **ignore:** update zigbee-herdsman to 10.3.0 ([#32193](https://github.com/Koenkk/zigbee2mqtt/issues/32193)) ([ea6ccac](https://github.com/Koenkk/zigbee2mqtt/commit/ea6ccac9380af1d9f5672174b2545409f36c2769))
* **ignore:** update zigbee-herdsman to 10.4.0 ([#32249](https://github.com/Koenkk/zigbee2mqtt/issues/32249)) ([582f153](https://github.com/Koenkk/zigbee2mqtt/commit/582f1538961af9c1b6434e6a1bc4acebc63a2541))
* **ignore:** update zigbee-herdsman-converters to 26.61.2 ([#32180](https://github.com/Koenkk/zigbee2mqtt/issues/32180)) ([babf7a1](https://github.com/Koenkk/zigbee2mqtt/commit/babf7a17d4617217297eef45f6b8307265f9b921))
* **ignore:** update zigbee-herdsman-converters to 26.62.0 ([#32194](https://github.com/Koenkk/zigbee2mqtt/issues/32194)) ([120f6ac](https://github.com/Koenkk/zigbee2mqtt/commit/120f6ac779c9fda6914fc68c56674e589c0e82ba))
* **ignore:** update zigbee-herdsman-converters to 26.63.0 ([#32252](https://github.com/Koenkk/zigbee2mqtt/issues/32252)) ([68daf4f](https://github.com/Koenkk/zigbee2mqtt/commit/68daf4f1deafad3ee6e542acbd77d403340e2caa))
## [2.11.0](https://github.com/Koenkk/zigbee2mqtt/compare/2.10.1...2.11.0) (2026-06-01)
### Features
* allow to disable external JS extensions ([#31826](https://github.com/Koenkk/zigbee2mqtt/issues/31826)) ([15fd9b3](https://github.com/Koenkk/zigbee2mqtt/commit/15fd9b371e30ed352cf2de2d241a0d0f7fafa9d1))
### Bug Fixes
* **deps-dev:** bump tmp from 0.2.5 to 0.2.6 ([#32122](https://github.com/Koenkk/zigbee2mqtt/issues/32122)) ([7b9407d](https://github.com/Koenkk/zigbee2mqtt/commit/7b9407d020b9db1093b360f5f6e217bbab7afb63))
* **deps:** bump actions/dependency-review-action from 4 to 5 ([#31980](https://github.com/Koenkk/zigbee2mqtt/issues/31980)) ([4d9e950](https://github.com/Koenkk/zigbee2mqtt/commit/4d9e9504ef9c857a1985c79d3a6799552abc3aea))
* **deps:** bump brace-expansion from 2.0.2 to 5.0.6 ([#32038](https://github.com/Koenkk/zigbee2mqtt/issues/32038)) ([897aa8f](https://github.com/Koenkk/zigbee2mqtt/commit/897aa8fb7c82dbc9c906f986477c4c1673bb3199))
* **deps:** bump pnpm/action-setup from 5 to 6 ([#31698](https://github.com/Koenkk/zigbee2mqtt/issues/31698)) ([1a1c094](https://github.com/Koenkk/zigbee2mqtt/commit/1a1c09449c9d077ea06048d7e2ecea90ff850c9b))
* **deps:** bump ws from 8.20.0 to 8.20.1 ([#32041](https://github.com/Koenkk/zigbee2mqtt/issues/32041)) ([bf964cc](https://github.com/Koenkk/zigbee2mqtt/commit/bf964cc6d976d70e687844e5a09c4577763dc0d3))
* Fix default value of "optimistic" group setting ([#32054](https://github.com/Koenkk/zigbee2mqtt/issues/32054)) ([b31e8c2](https://github.com/Koenkk/zigbee2mqtt/commit/b31e8c286e7a77517a88e3e7eb2247e9a0fd2cc1))
* **ignore:** bump the minor-patch group with 2 updates ([#32137](https://github.com/Koenkk/zigbee2mqtt/issues/32137)) ([ac8c45f](https://github.com/Koenkk/zigbee2mqtt/commit/ac8c45f67f0a4aae2688e55b98dabdc23f73a259))
* **ignore:** bump the minor-patch group with 3 updates ([#31982](https://github.com/Koenkk/zigbee2mqtt/issues/31982)) ([9426c3e](https://github.com/Koenkk/zigbee2mqtt/commit/9426c3ea118de9e60326e8dc257a0c2474380f8c))
* **ignore:** bump the minor-patch group with 3 updates ([#32106](https://github.com/Koenkk/zigbee2mqtt/issues/32106)) ([e5f70d5](https://github.com/Koenkk/zigbee2mqtt/commit/e5f70d5ba82923f9f37fbda79bed08d581706129))
* **ignore:** update zigbee-herdsman to 10.0.8 ([#31945](https://github.com/Koenkk/zigbee2mqtt/issues/31945)) ([04a4365](https://github.com/Koenkk/zigbee2mqtt/commit/04a43652d6e131694e29e06ca646eeac37ed3c7d))
* **ignore:** update zigbee-herdsman to 10.1.0 ([#32027](https://github.com/Koenkk/zigbee2mqtt/issues/32027)) ([36c4db7](https://github.com/Koenkk/zigbee2mqtt/commit/36c4db7f8f4e3438f6ebb82cd6ecadf6c54e231e))
* **ignore:** update zigbee-herdsman-converters to 26.47.0 ([#31946](https://github.com/Koenkk/zigbee2mqtt/issues/31946)) ([7a05464](https://github.com/Koenkk/zigbee2mqtt/commit/7a05464f0355380ee855415bc270632263e3f5d3))
* **ignore:** update zigbee-herdsman-converters to 26.48.0 ([#31959](https://github.com/Koenkk/zigbee2mqtt/issues/31959)) ([502030c](https://github.com/Koenkk/zigbee2mqtt/commit/502030c0168c8ba6eaf7495a9014489f2f7a7c50))
* **ignore:** update zigbee-herdsman-converters to 26.49.0 ([#31976](https://github.com/Koenkk/zigbee2mqtt/issues/31976)) ([32499d1](https://github.com/Koenkk/zigbee2mqtt/commit/32499d14d5f0c9eada160d596d6be92da6c4af95))
* **ignore:** update zigbee-herdsman-converters to 26.50.0 ([#31988](https://github.com/Koenkk/zigbee2mqtt/issues/31988)) ([6ea21c3](https://github.com/Koenkk/zigbee2mqtt/commit/6ea21c383b4801f28e30b022194e71375db94b17))
* **ignore:** update zigbee-herdsman-converters to 26.51.0 ([#31999](https://github.com/Koenkk/zigbee2mqtt/issues/31999)) ([744e4a6](https://github.com/Koenkk/zigbee2mqtt/commit/744e4a6d5c4dd179e7c3d1a2b4fbcdc820195547))
* **ignore:** update zigbee-herdsman-converters to 26.51.1 ([#32016](https://github.com/Koenkk/zigbee2mqtt/issues/32016)) ([5458e50](https://github.com/Koenkk/zigbee2mqtt/commit/5458e5084def411aaad9af08e415acb6bc16d181))
* **ignore:** update zigbee-herdsman-converters to 26.52.0 ([#32040](https://github.com/Koenkk/zigbee2mqtt/issues/32040)) ([1e114de](https://github.com/Koenkk/zigbee2mqtt/commit/1e114dee70b8079eda31d4d0cc52fa073a772798))
* **ignore:** update zigbee-herdsman-converters to 26.53.0 ([#32051](https://github.com/Koenkk/zigbee2mqtt/issues/32051)) ([52201c9](https://github.com/Koenkk/zigbee2mqtt/commit/52201c96bf897af9bcad30435575923a0452b6dd))
* **ignore:** update zigbee-herdsman-converters to 26.54.0 ([#32065](https://github.com/Koenkk/zigbee2mqtt/issues/32065)) ([2c5b536](https://github.com/Koenkk/zigbee2mqtt/commit/2c5b53613448ae4c6548f874a36acc1226d53ec3))
* **ignore:** update zigbee-herdsman-converters to 26.55.0 ([#32068](https://github.com/Koenkk/zigbee2mqtt/issues/32068)) ([7075b78](https://github.com/Koenkk/zigbee2mqtt/commit/7075b78a462a0d1f76cf91cc78601d90edfd8b4e))
* **ignore:** update zigbee-herdsman-converters to 26.56.0 ([#32087](https://github.com/Koenkk/zigbee2mqtt/issues/32087)) ([56aab4e](https://github.com/Koenkk/zigbee2mqtt/commit/56aab4e59c4ea7bdbd93a1ed6632171994d8ed56))
* **ignore:** update zigbee-herdsman-converters to 26.57.0 ([#32113](https://github.com/Koenkk/zigbee2mqtt/issues/32113)) ([7a1c2d5](https://github.com/Koenkk/zigbee2mqtt/commit/7a1c2d52f0b0699854f3c8c9375761cb0b22ac55))
* **ignore:** update zigbee-herdsman-converters to 26.58.0 ([#32126](https://github.com/Koenkk/zigbee2mqtt/issues/32126)) ([e2b4911](https://github.com/Koenkk/zigbee2mqtt/commit/e2b49113c7932c4cd8ef7b23a27e3cd5d4e7150d))
* **ignore:** update zigbee-herdsman-converters to 26.59.1 ([#32136](https://github.com/Koenkk/zigbee2mqtt/issues/32136)) ([a8364ad](https://github.com/Koenkk/zigbee2mqtt/commit/a8364ad67b0dcbde93b1d6445c269a821321e9b3))
* **ignore:** update zigbee-herdsman-converters to 26.60.0 ([#32150](https://github.com/Koenkk/zigbee2mqtt/issues/32150)) ([c6e13cc](https://github.com/Koenkk/zigbee2mqtt/commit/c6e13ccb45666f9604e8c9c9655bbe7ea682712d))
* **ignore:** update zigbee-herdsman-converters to 26.61.1 ([#32156](https://github.com/Koenkk/zigbee2mqtt/issues/32156)) ([58feb78](https://github.com/Koenkk/zigbee2mqtt/commit/58feb783626fd2429bea0f8a0db37bdc107c431b))
* Prevent invalid external JS file name on save ([#32037](https://github.com/Koenkk/zigbee2mqtt/issues/32037)) ([bbcbed1](https://github.com/Koenkk/zigbee2mqtt/commit/bbcbed12d53408eaa8ceec110c7071fff8e158e4))
* Use Jinja-safe property access in HA discovery templates ([#31930](https://github.com/Koenkk/zigbee2mqtt/issues/31930)) ([c4fd415](https://github.com/Koenkk/zigbee2mqtt/commit/c4fd415cbe7b01719679b1a96d2b6e07b0793125))
## [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)
+4 -10
View File
@@ -1,14 +1,8 @@
# Contributing to Zigbee2MQTT
> [!WARNING]
> Any AI-driven pull request with more than 500 lines of code will not be considered. If wanting to submit something that requires more, split the work into easily review-able pull requests that can be introduced in increments (e.g. pre-refactor, base feature, additional features).
Everybody is invited and welcomed to contribute to Zigbee2MQTT.
Zigbee2MQTT is written in TypeScript.
It uses [zigbee-herdsman](https://github.com/koenkk/zigbee-herdsman) for communication with the adapter/coordinator and [zigbee-herdsman-converters](https://github.com/koenkk/zigbee-herdsman-converters) to provide device-specific definitions.
Everybody is invited and welcome to contribute to Zigbee2MQTT. Zigbee2MQTT is written in JavaScript and is based upon [zigbee-herdsman](https://github.com/koenkk/zigbee-herdsman) and [zigbee-herdsman-converters](https://github.com/koenkk/zigbee-herdsman-converters). Zigbee-herdsman-converters contains all device definitions, zigbee-herdsman is responsible for handling all communication with the adapter.
- Pull requests are always created against the [**dev**](https://github.com/Koenkk/zigbee2mqtt/tree/dev) branch.
- Easiest way to start developing Zigbee2MQTT is by setting up a development environment (a.k.a. bare-metal installation). You can follow this [guide](https://www.zigbee2mqtt.io/guide/installation/01_linux.html) to do this.
- You can run the tests locally by executing `pnpm test`. Zigbee2MQTT enforces 100% code coverage, in case you add new code check if your code is covered by running `pnpm run test:coverage`. The coverage report can be found under `coverage/lcov-report/index.html`.
- Linting & formatting is also enforced and can be run with `pnpm run check` (can use `pnpm run check:w` to fix small issues automatically).
- If you want to add support for a new device no change to Zigbee2MQTT has to be made, only to zigbee-herdsman-converters. You can find a guide for it [here](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html).
- Easiest way to start developing Zigbee2MQTT is by setting up a development environment (aka bare-metal installation). You can follow this [guide](https://www.zigbee2mqtt.io/guide/installation/01_linux.html) to do this.
- You can run the tests locally by executing `pnpm test`. Zigbee2MQTT enforces 100% code coverage, in case you add new code check if your code is covered by running `pnpm run test:coverage`. The coverage report can be found under `coverage/lcov-report/index.html`. Linting is also enforced and can be run with `pnpm run eslint`.
- When you want to add support for a new device no changes to Zigbee2MQTT have to be made, only to zigbee-herdsman-converters. You can find a guide for it [here](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html).
+1 -1
View File
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.16/schema.json",
"$schema": "https://biomejs.dev/schemas/2.4.10/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
+17 -25
View File
@@ -50,6 +50,7 @@ export class Controller {
this.state = new State(this.eventBus, this.zigbee);
this.restartCallback = restartCallback;
this.exitCallback = exitCallback;
// Initialize extensions.
this.extensionArgs = [
this.zigbee,
@@ -61,29 +62,22 @@ export class Controller {
this.restartCallback,
this.addExtension,
];
this.extensions = new Set();
if (settings.get().advanced.enable_external_js) {
this.extensions.add(new ExtensionExternalConverters(...this.extensionArgs));
} else {
logger.info("External JS (converters/extensions) is disabled");
}
this.extensions.add(new ExtensionOnEvent(...this.extensionArgs));
this.extensions.add(new ExtensionBridge(...this.extensionArgs));
this.extensions.add(new ExtensionPublish(...this.extensionArgs));
this.extensions.add(new ExtensionReceive(...this.extensionArgs));
this.extensions.add(new ExtensionConfigure(...this.extensionArgs));
this.extensions.add(new ExtensionNetworkMap(...this.extensionArgs));
this.extensions.add(new ExtensionGroups(...this.extensionArgs));
this.extensions.add(new ExtensionBind(...this.extensionArgs));
this.extensions.add(new ExtensionOTAUpdate(...this.extensionArgs));
this.extensions.add(new ExtensionAvailability(...this.extensionArgs));
this.extensions.add(new ExtensionHealth(...this.extensionArgs));
if (settings.get().advanced.enable_external_js) {
this.extensions.add(new ExtensionExternalExtensions(...this.extensionArgs));
}
this.extensions = new Set([
new ExtensionExternalConverters(...this.extensionArgs),
new ExtensionOnEvent(...this.extensionArgs),
new ExtensionBridge(...this.extensionArgs),
new ExtensionPublish(...this.extensionArgs),
new ExtensionReceive(...this.extensionArgs),
new ExtensionConfigure(...this.extensionArgs),
new ExtensionNetworkMap(...this.extensionArgs),
new ExtensionGroups(...this.extensionArgs),
new ExtensionBind(...this.extensionArgs),
new ExtensionOTAUpdate(...this.extensionArgs),
new ExtensionExternalExtensions(...this.extensionArgs),
new ExtensionAvailability(...this.extensionArgs),
new ExtensionHealth(...this.extensionArgs),
]);
}
async start(): Promise<void> {
@@ -253,12 +247,10 @@ export class Controller {
}
const existingExtension = this.getExtension(name);
if (existingExtension) {
await this.removeExtension(existingExtension);
}
this.extensions.add(extension);
await this.extensions.add(extension);
} else {
switch (name) {
case "Frontend": {
-6
View File
@@ -176,13 +176,7 @@ export default abstract class ExternalJSExtension<M> extends Extension {
}
const {name, code} = message;
if (!name.endsWith(".mjs") && !name.endsWith(".js") && !name.endsWith(".cjs")) {
return utils.getResponse(message, {}, "JavaScript file must have '.mjs', '.js' or '.cjs' extension");
}
const filePath = this.getFilePath(name, true);
try {
fs.writeFileSync(filePath, code, "utf8");
this.symlinkNodeModulesIfNecessary();
+3 -2
View File
@@ -5,6 +5,7 @@ import {createServer} from "node:http";
import {createServer as createSecureServer} from "node:https";
import type {Socket} from "node:net";
import {posix} from "node:path";
import {parse} from "node:url";
import bind from "bind-decorator";
import expressStaticGzip from "express-static-gzip";
import finalhandler from "finalhandler";
@@ -156,10 +157,10 @@ export class Frontend extends Extension {
@bind private onUpgrade(request: IncomingMessage, socket: Socket, head: Buffer): void {
this.wss.handleUpgrade(request, socket, head, (ws) => {
// biome-ignore lint/style/noNonNullAssertion: `Only valid for request obtained from Server`
const {searchParams} = new URL(request.url!, "http://localhost"); // dummy base, may not be absolute
const {query} = parse(request.url!, true);
const authToken = settings.get().frontend.auth_token;
if (!authToken || authToken === searchParams.get("token")) {
if (!authToken || authToken === query.token) {
this.wss.emit("connection", ws, request);
} else {
ws.close(4401, "Unauthorized");
+32 -32
View File
@@ -590,7 +590,7 @@ export class HomeAssistant extends Extension {
name: endpointName ? utils.capitalize(endpointName) : null,
payload_off: state.value_off,
payload_on: state.value_on,
value_template: `{{ value_json["${property}"] }}`,
value_template: `{{ value_json.${property} }}`,
command_topic: true,
command_topic_prefix: endpointName,
},
@@ -636,7 +636,7 @@ export class HomeAssistant extends Extension {
max_temp: setpoint.value_max.toString(),
// Temperature
current_temperature_topic: true,
current_temperature_template: `{{ value_json["${temperature.property}"] }}`,
current_temperature_template: `{{ value_json.${temperature.property} }}`,
command_topic_prefix: endpointName,
},
};
@@ -650,7 +650,7 @@ export class HomeAssistant extends Extension {
mode.values.splice(mode.values.indexOf("sleep"), 1);
}
discoveryEntry.discovery_payload.mode_state_topic = true;
discoveryEntry.discovery_payload.mode_state_template = `{{ value_json["${mode.property}"] }}`;
discoveryEntry.discovery_payload.mode_state_template = `{{ value_json.${mode.property} }}`;
discoveryEntry.discovery_payload.modes = mode.values;
discoveryEntry.discovery_payload.mode_command_topic = true;
}
@@ -659,20 +659,20 @@ export class HomeAssistant extends Extension {
if (state) {
discoveryEntry.mockProperties.push({property: state.property, value: null});
discoveryEntry.discovery_payload.action_topic = true;
discoveryEntry.discovery_payload.action_template = `{% set values = {None:None,'idle':'idle','heat':'heating','cool':'cooling','fan_only':'fan'} %}{{ values[value_json["${state.property}"]] }}`;
discoveryEntry.discovery_payload.action_template = `{% set values = {None:None,'idle':'idle','heat':'heating','cool':'cooling','fan_only':'fan'} %}{{ values[value_json.${state.property}] }}`;
}
const coolingSetpoint = (firstExpose as zhc.Climate).features.find((f) => f.name === "occupied_cooling_setpoint");
if (coolingSetpoint) {
discoveryEntry.discovery_payload.temperature_low_command_topic = setpoint.name;
discoveryEntry.discovery_payload.temperature_low_state_template = `{{ value_json["${setpoint.property}"] }}`;
discoveryEntry.discovery_payload.temperature_low_state_template = `{{ value_json.${setpoint.property} }}`;
discoveryEntry.discovery_payload.temperature_low_state_topic = true;
discoveryEntry.discovery_payload.temperature_high_command_topic = coolingSetpoint.name;
discoveryEntry.discovery_payload.temperature_high_state_template = `{{ value_json["${coolingSetpoint.property}"] }}`;
discoveryEntry.discovery_payload.temperature_high_state_template = `{{ value_json.${coolingSetpoint.property} }}`;
discoveryEntry.discovery_payload.temperature_high_state_topic = true;
} else {
discoveryEntry.discovery_payload.temperature_command_topic = setpoint.name;
discoveryEntry.discovery_payload.temperature_state_template = `{{ value_json["${setpoint.property}"] }}`;
discoveryEntry.discovery_payload.temperature_state_template = `{{ value_json.${setpoint.property} }}`;
discoveryEntry.discovery_payload.temperature_state_topic = true;
}
@@ -680,7 +680,7 @@ export class HomeAssistant extends Extension {
if (fanMode) {
discoveryEntry.discovery_payload.fan_modes = fanMode.values;
discoveryEntry.discovery_payload.fan_mode_command_topic = true;
discoveryEntry.discovery_payload.fan_mode_state_template = `{{ value_json["${fanMode.property}"] }}`;
discoveryEntry.discovery_payload.fan_mode_state_template = `{{ value_json.${fanMode.property} }}`;
discoveryEntry.discovery_payload.fan_mode_state_topic = true;
}
@@ -688,7 +688,7 @@ export class HomeAssistant extends Extension {
if (swingMode) {
discoveryEntry.discovery_payload.swing_modes = swingMode.values;
discoveryEntry.discovery_payload.swing_mode_command_topic = true;
discoveryEntry.discovery_payload.swing_mode_state_template = `{{ value_json["${swingMode.property}"] }}`;
discoveryEntry.discovery_payload.swing_mode_state_template = `{{ value_json.${swingMode.property} }}`;
discoveryEntry.discovery_payload.swing_mode_state_topic = true;
}
@@ -696,7 +696,7 @@ export class HomeAssistant extends Extension {
if (preset) {
discoveryEntry.discovery_payload.preset_modes = preset.values;
discoveryEntry.discovery_payload.preset_mode_command_topic = "preset";
discoveryEntry.discovery_payload.preset_mode_value_template = `{{ value_json["${preset.property}"] }}`;
discoveryEntry.discovery_payload.preset_mode_value_template = `{{ value_json.${preset.property} }}`;
discoveryEntry.discovery_payload.preset_mode_state_topic = true;
}
@@ -710,7 +710,7 @@ export class HomeAssistant extends Extension {
mockProperties: [{property: tempCalibration.property, value: null}],
discovery_payload: {
name: endpointName ? `${tempCalibration.label} ${endpointName}` : tempCalibration.label,
value_template: `{{ value_json["${tempCalibration.property}"] }}`,
value_template: `{{ value_json.${tempCalibration.property} }}`,
command_topic: true,
command_topic_prefix: endpointName,
command_topic_postfix: tempCalibration.property,
@@ -735,7 +735,7 @@ export class HomeAssistant extends Extension {
mockProperties: [{property: piHeatingDemand.property, value: null}],
discovery_payload: {
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}),
icon: "mdi:radiator",
},
@@ -766,7 +766,7 @@ export class HomeAssistant extends Extension {
mockProperties: [{property: piCoolingDemand.property, value: null}],
discovery_payload: {
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}),
entity_category: "diagnostic",
icon: "mdi:air-conditioner",
@@ -788,7 +788,7 @@ export class HomeAssistant extends Extension {
mockProperties: [{property: localTemperature.property, value: null}],
discovery_payload: {
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}),
device_class: "temperature",
state_class: "measurement",
@@ -800,7 +800,7 @@ export class HomeAssistant extends Extension {
const currentHumidity = allExposes?.filter(isNumericExpose).find((e) => e.name === "humidity" && e.access & ACCESS_STATE);
if (currentHumidity) {
discoveryEntry.discovery_payload.current_humidity_template = `{{ value_json["${currentHumidity.property}"] }}`;
discoveryEntry.discovery_payload.current_humidity_template = `{{ value_json.${currentHumidity.property} }}`;
discoveryEntry.discovery_payload.current_humidity_topic = true;
}
@@ -820,7 +820,7 @@ export class HomeAssistant extends Extension {
name: endpointName ? utils.capitalize(endpointName) : null,
command_topic_prefix: endpointName,
command_topic: true,
value_template: `{{ value_json["${state.property}"] }}`,
value_template: `{{ value_json.${state.property} }}`,
state_locked: state.value_on,
state_unlocked: state.value_off,
/* v8 ignore next */
@@ -863,7 +863,7 @@ export class HomeAssistant extends Extension {
// The movement direction is calculated (assumed) in this case.
if (running) {
assert(position, `Cover must have 'position' when it has 'running'`);
discoveryEntry.discovery_payload.value_template = `{% if "${featurePropertyWithoutEndpoint(running)}" in value_json and value_json["${featurePropertyWithoutEndpoint(running)}"] %} {% if value_json["${featurePropertyWithoutEndpoint(position)}"] > 0 %} closing {% else %} opening {% endif %} {% else %} stopped {% endif %}`;
discoveryEntry.discovery_payload.value_template = `{% if "${featurePropertyWithoutEndpoint(running)}" in value_json and value_json.${featurePropertyWithoutEndpoint(running)} %} {% if value_json.${featurePropertyWithoutEndpoint(position)} > 0 %} closing {% else %} opening {% endif %} {% else %} stopped {% endif %}`;
}
// If curtains have `motor_state` or `moving` property, lookup for possible
@@ -877,13 +877,13 @@ export class HomeAssistant extends Extension {
discoveryEntry.discovery_payload.state_opening = openingState;
discoveryEntry.discovery_payload.state_closing = closingState;
discoveryEntry.discovery_payload.state_stopped = stoppedState;
discoveryEntry.discovery_payload.value_template = `{% if "${featurePropertyWithoutEndpoint(motorState)}" in value_json and value_json["${featurePropertyWithoutEndpoint(motorState)}"] %} {{ value_json["${featurePropertyWithoutEndpoint(motorState)}"] }} {% else %} ${stoppedState} {% endif %}`;
discoveryEntry.discovery_payload.value_template = `{% if "${featurePropertyWithoutEndpoint(motorState)}" in value_json and value_json.${featurePropertyWithoutEndpoint(motorState)} %} {{ value_json.${featurePropertyWithoutEndpoint(motorState)} }} {% else %} ${stoppedState} {% endif %}`;
}
}
// If curtains do not have `running`, `motor_state` or `moving` properties.
if (!discoveryEntry.discovery_payload.value_template) {
discoveryEntry.discovery_payload.value_template = `{{ value_json["${featurePropertyWithoutEndpoint(state)}"] }}`;
discoveryEntry.discovery_payload.value_template = `{{ value_json.${featurePropertyWithoutEndpoint(state)} }}`;
discoveryEntry.discovery_payload.state_open = "OPEN";
discoveryEntry.discovery_payload.state_closed = "CLOSE";
discoveryEntry.discovery_payload.state_stopped = "STOP";
@@ -898,7 +898,7 @@ export class HomeAssistant extends Extension {
if (position) {
discoveryEntry.discovery_payload = {
...discoveryEntry.discovery_payload,
position_template: `{{ value_json["${featurePropertyWithoutEndpoint(position)}"] }}`,
position_template: `{{ value_json.${featurePropertyWithoutEndpoint(position)} }}`,
set_position_template: `{ "${getProperty(position)}": {{ position }} }`,
set_position_topic: true,
position_topic: true,
@@ -910,7 +910,7 @@ export class HomeAssistant extends Extension {
...discoveryEntry.discovery_payload,
tilt_command_topic: true,
tilt_status_topic: true,
tilt_status_template: `{{ value_json["${featurePropertyWithoutEndpoint(tilt)}"] }}`,
tilt_status_template: `{{ value_json.${featurePropertyWithoutEndpoint(tilt)} }}`,
};
}
@@ -975,14 +975,14 @@ export class HomeAssistant extends Extension {
discoveryEntry.discovery_payload.percentage_state_topic = true;
discoveryEntry.discovery_payload.percentage_command_topic = "fan_mode";
discoveryEntry.discovery_payload.percentage_value_template = `{{ {${percentValues}}[value_json["${modeEmulatedSpeed.property}"]] | default('None') }}`;
discoveryEntry.discovery_payload.percentage_value_template = `{{ {${percentValues}}[value_json.${modeEmulatedSpeed.property}] | default('None') }}`;
discoveryEntry.discovery_payload.percentage_command_template = `{{ {${percentCommands}}[value] | default('') }}`;
discoveryEntry.discovery_payload.speed_range_min = 1;
discoveryEntry.discovery_payload.speed_range_max = speeds.length - 1;
assert(presets.length !== 0);
discoveryEntry.discovery_payload.preset_mode_state_topic = true;
discoveryEntry.discovery_payload.preset_mode_command_topic = "fan_mode";
discoveryEntry.discovery_payload.preset_mode_value_template = `{{ value_json["${modeEmulatedSpeed.property}"] if value_json["${modeEmulatedSpeed.property}"] in [${presetList}] else 'None' | default('None') }}`;
discoveryEntry.discovery_payload.preset_mode_value_template = `{{ value_json.${modeEmulatedSpeed.property} if value_json.${modeEmulatedSpeed.property} in [${presetList}] else 'None' | default('None') }}`;
discoveryEntry.discovery_payload.preset_modes = presets;
// Emulate state based on mode
@@ -991,7 +991,7 @@ export class HomeAssistant extends Extension {
} else if (nativeSpeed) {
discoveryEntry.discovery_payload.percentage_state_topic = true;
discoveryEntry.discovery_payload.percentage_command_topic = "speed";
discoveryEntry.discovery_payload.percentage_value_template = `{{ value_json["${nativeSpeed.property}"] | default('None') }}`;
discoveryEntry.discovery_payload.percentage_value_template = `{{ value_json.${nativeSpeed.property} | default('None') }}`;
discoveryEntry.discovery_payload.percentage_command_template = `{{ value | default('') }}`;
discoveryEntry.discovery_payload.speed_range_min = nativeSpeed.value_min;
discoveryEntry.discovery_payload.speed_range_max = nativeSpeed.value_max;
@@ -1021,8 +1021,8 @@ export class HomeAssistant extends Extension {
name: endpointName ? /* v8 ignore next */ `${firstExpose.label} ${endpointName}` : firstExpose.label,
value_template:
typeof firstExpose.value_on === "boolean"
? `{% if value_json["${firstExpose.property}"] %}true{% else %}false{% endif %}`
: `{{ value_json["${firstExpose.property}"] }}`,
? `{% if value_json.${firstExpose.property} %}true{% else %}false{% endif %}`
: `{{ value_json.${firstExpose.property} }}`,
payload_on: firstExpose.value_on.toString(),
payload_off: firstExpose.value_off.toString(),
command_topic: true,
@@ -1040,7 +1040,7 @@ export class HomeAssistant extends Extension {
mockProperties: [{property: firstExpose.property, value: null}],
discovery_payload: {
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_off: firstExpose.value_off,
...(BINARY_DISCOVERY_LOOKUP[firstExpose.name] || {}),
@@ -1065,7 +1065,7 @@ export class HomeAssistant extends Extension {
mockProperties: [{property: firstExpose.property, value: null}],
discovery_payload: {
name: endpointName ? `${firstExpose.label} ${endpointName}` : firstExpose.label,
value_template: `{{ value_json["${firstExpose.property}"] }}`,
value_template: `{{ value_json.${firstExpose.property} }}`,
command_topic: true,
command_topic_prefix: endpointName,
command_topic_postfix: firstExpose.property,
@@ -1116,7 +1116,7 @@ export class HomeAssistant extends Extension {
mockProperties: [{property: firstExpose.property, value: null}],
discovery_payload: {
name: endpointName ? `${firstExpose.label} ${endpointName}` : firstExpose.label,
value_template: `{{ value_json["${firstExpose.property}"] }}`,
value_template: `{{ value_json.${firstExpose.property} }}`,
enabled_by_default: !allowsSet,
...(firstExpose.unit && {unit_of_measurement: firstExpose.unit}),
...NUMERIC_DISCOVERY_LOOKUP[key],
@@ -1170,7 +1170,7 @@ export class HomeAssistant extends Extension {
}
}
const valueTemplate = firstExpose.access & ACCESS_STATE ? `{{ value_json["${firstExpose.property}"] }}` : undefined;
const valueTemplate = firstExpose.access & ACCESS_STATE ? `{{ value_json.${firstExpose.property} }}` : undefined;
/**
* If enum has only one item and has SET access then expose as BUTTON entity.
@@ -1299,7 +1299,7 @@ export class HomeAssistant extends Extension {
discovery_payload: {
name: endpointName ? `${firstExposeTyped.label} ${endpointName}` : firstExposeTyped.label,
state_topic: firstExposeTyped.access & ACCESS_STATE,
value_template: `{{ value_json["${firstExposeTyped.property}"] }}`,
value_template: `{{ value_json.${firstExposeTyped.property} }}`,
command_topic_prefix: endpointName,
command_topic: true,
command_topic_postfix: firstExposeTyped.property,
@@ -1317,7 +1317,7 @@ export class HomeAssistant extends Extension {
name: endpointName ? `${firstExposeTyped.label} ${endpointName}` : firstExposeTyped.label,
// Truncate text if it's too long
// 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) }}`,
...LIST_DISCOVERY_LOOKUP[firstExposeTyped.name],
},
});
+12 -29
View File
@@ -50,7 +50,7 @@ function writeFirmwareHexToDataDir(hex: string, fileName: string | undefined, de
export default class OTAUpdate extends Extension {
#topicRegex = new RegExp(
`^${settings.get().mqtt.base_topic}/bridge/request/device/ota_update/(update|check|schedule|unschedule)/?(downgrade|abort)?`,
`^${settings.get().mqtt.base_topic}/bridge/request/device/ota_update/(update|check|schedule|unschedule)/?(downgrade)?`,
"i",
);
#inProgress = new Set<string>();
@@ -248,7 +248,6 @@ export default class OTAUpdate extends Extension {
| Zigbee2MQTTAPI["bridge/request/device/ota_update/check/downgrade"]
| Zigbee2MQTTAPI["bridge/request/device/ota_update/update"]
| Zigbee2MQTTAPI["bridge/request/device/ota_update/update/downgrade"]
| Zigbee2MQTTAPI["bridge/request/device/ota_update/update/abort"]
| Zigbee2MQTTAPI["bridge/request/device/ota_update/schedule"]
| Zigbee2MQTTAPI["bridge/request/device/ota_update/schedule/downgrade"]
| Zigbee2MQTTAPI["bridge/request/device/ota_update/unschedule"];
@@ -259,31 +258,18 @@ export default class OTAUpdate extends Extension {
assert(message.id, "Invalid payload");
}
const id = (messageObject ? message.id : message) as string;
const device = this.zigbee.resolveEntity(id);
const ID = (messageObject ? message.id : message) as string;
const device = this.zigbee.resolveEntity(ID);
const type = topicMatch[1] as "check" | "update" | "schedule" | "unschedule";
const downgrade = topicMatch[2] === "downgrade";
const abort = topicMatch[2] === "abort";
let error: string | undefined;
let errorStack: string | undefined;
if (!(device instanceof Device)) {
error = `Device '${id}' does not exist`;
error = `Device '${ID}' does not exist`;
} else if (this.#inProgress.has(device.ieeeAddr)) {
if (abort) {
device.zh.abortOta();
this.#inProgress.delete(device.ieeeAddr);
// cleanup same as a fail
this.#removeProgressAndRemainingFromState(device);
await this.publishEntityState(device, this.#getEntityPublishPayload(device, "available"));
await this.mqtt.publish(
"bridge/response/device/ota_update/update/abort",
stringify(utils.getResponse<"bridge/response/device/ota_update/update/abort">(message, {id})),
);
} else {
// also guards against scheduling while check/update op in progress that could result in undesired OTA state
error = `OTA update or check for update already in progress for '${device.name}'`;
}
// also guards against scheduling while check/update op in progress that could result in undesired OTA state
error = `OTA update or check for update already in progress for '${device.name}'`;
} else {
switch (type) {
case "check": {
@@ -318,7 +304,7 @@ export default class OTAUpdate extends Extension {
this.#lastChecked.set(device.ieeeAddr, Date.now());
const response = utils.getResponse<"bridge/response/device/ota_update/check">(message, {
id,
id: ID,
update_available: availableResult.available,
downgrade: source.downgrade,
source: availableResult.availableMeta?.url,
@@ -335,11 +321,6 @@ export default class OTAUpdate extends Extension {
}
case "update": {
if (abort) {
error = `No OTA in progress to abort for device '${device.name}'`;
break;
}
this.#inProgress.add(device.ieeeAddr);
const otaSettings = settings.get().ota;
@@ -397,7 +378,7 @@ export default class OTAUpdate extends Extension {
const firmwareTo = await this.#readSoftwareBuildIDAndDateCode(device);
const response = utils.getResponse<"bridge/response/device/ota_update/update">(message, {
id,
id: ID,
from: {
file_version: fromVersion,
software_build_id: firmwareFrom?.softwareBuildID,
@@ -446,7 +427,7 @@ export default class OTAUpdate extends Extension {
device.zh.scheduleOta(source);
await this.publishEntityState(device, this.#getEntityPublishPayload(device, "scheduled"));
const response = utils.getResponse<"bridge/response/device/ota_update/schedule">(message, {id, url: source.url});
const response = utils.getResponse<"bridge/response/device/ota_update/schedule">(message, {id: ID, url: source.url});
await this.mqtt.publish("bridge/response/device/ota_update/schedule", stringify(response));
@@ -461,7 +442,9 @@ export default class OTAUpdate extends Extension {
device.zh.unscheduleOta();
await this.publishEntityState(device, this.#getEntityPublishPayload(device, "idle"));
const response = utils.getResponse<"bridge/response/device/ota_update/unschedule">(message, {id});
const response = utils.getResponse<"bridge/response/device/ota_update/unschedule">(message, {
id: ID,
});
await this.mqtt.publish("bridge/response/device/ota_update/unschedule", stringify(response));
-12
View File
@@ -215,8 +215,6 @@ export interface Zigbee2MQTTSettings {
timestamp_format: string;
output: "json" | "attribute" | "attribute_and_json";
transmit_power?: number;
/** 3.0: default to false (JSON schema & settings `defaults`) */
enable_external_js: boolean;
};
health: {
/** in minutes */
@@ -677,14 +675,6 @@ export interface Zigbee2MQTTAPI {
default_maximum_data_size?: number | null;
};
"bridge/request/device/ota_update/update/abort": {
id: string;
};
"bridge/response/device/ota_update/update/abort": {
id: string;
};
"bridge/response/device/ota_update/update": {
id: string;
from:
@@ -1009,7 +999,6 @@ export type Zigbee2MQTTRequestEndpoints =
| "bridge/request/device/ota_update/check/downgrade"
| "bridge/request/device/ota_update/update"
| "bridge/request/device/ota_update/update/downgrade"
| "bridge/request/device/ota_update/update/abort"
| "bridge/request/device/ota_update/schedule"
| "bridge/request/device/ota_update/schedule/downgrade"
| "bridge/request/device/ota_update/unschedule"
@@ -1059,7 +1048,6 @@ export type Zigbee2MQTTResponseEndpoints =
| "bridge/response/device/remove"
| "bridge/response/device/ota_update/check"
| "bridge/response/device/ota_update/update"
| "bridge/response/device/ota_update/update/abort"
| "bridge/response/device/ota_update/schedule"
| "bridge/response/device/ota_update/unschedule"
| "bridge/response/device/interview"
+4 -18
View File
@@ -800,7 +800,7 @@
"requiresRestart": true,
"minimum": -128,
"maximum": 127,
"description": "Transmit power of adapter, in dBm (max is often 20, refer to chip specifications)"
"description": "Transmit power of adapter, only available for Z-Stack (CC253*/CC2652/CC1352) adapters, CC2652 = 5dbm, CC1352 max is = 20dbm (5dbm default)"
},
"output": {
"type": "string",
@@ -808,13 +808,6 @@
"title": "MQTT output type",
"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)",
"default": "json"
},
"enable_external_js": {
"type": "boolean",
"title": "Enable external JS",
"description": "Enable external JavaScript (extensions and converters) that can execute arbitrary user-provided code. WARNING: If unused, it is advised to disable this.",
"default": true,
"requiresRestart": true
}
}
},
@@ -855,8 +848,7 @@
"retain": {
"type": "boolean",
"title": "Retain",
"description": "Retain MQTT messages of this device",
"default": false
"description": "Retain MQTT messages of this device"
},
"disabled": {
"type": "boolean",
@@ -961,16 +953,10 @@
"type": "string"
},
"retain": {
"type": "boolean",
"title": "Retain",
"description": "Retain MQTT messages of this group",
"default": false
"type": "boolean"
},
"optimistic": {
"type": "boolean",
"title": "Optimistic",
"description": "Publish the expected state of group members after set",
"default": true
"type": "boolean"
},
"qos": {
"type": ["number"],
-2
View File
@@ -113,7 +113,6 @@ export const defaults = {
network_key: [1, 3, 5, 7, 9, 11, 13, 15, 0, 2, 4, 6, 8, 10, 12, 13],
timestamp_format: "YYYY-MM-DD HH:mm:ss",
output: "json",
enable_external_js: true,
},
health: {
interval: 10,
@@ -168,7 +167,6 @@ export function writeMinimalDefaults(): void {
network_key: "GENERATE",
pan_id: "GENERATE",
ext_pan_id: "GENERATE",
enable_external_js: false,
},
frontend: {
enabled: defaults.frontend.enabled,
+12 -12
View File
@@ -1,6 +1,6 @@
{
"name": "zigbee2mqtt",
"version": "2.12.0",
"version": "2.9.2-dev",
"description": "Zigbee to MQTT bridge using Zigbee-herdsman",
"main": "index.js",
"types": "dist/types/api.d.ts",
@@ -42,43 +42,43 @@
},
"homepage": "https://koenkk.github.io/zigbee2mqtt",
"dependencies": {
"ajv": "^8.20.0",
"ajv": "^8.18.0",
"bind-decorator": "^1.0.11",
"debounce": "^3.0.0",
"express-static-gzip": "^3.0.1",
"express-static-gzip": "^3.0.0",
"fast-deep-equal": "^3.1.3",
"finalhandler": "^2.1.1",
"humanize-duration": "^3.33.2",
"js-yaml": "^4.2.0",
"js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"jszip": "^3.10.1",
"mqtt": "^5.15.1",
"object-assign-deep": "^0.4.0",
"rimraf": "^6.1.3",
"semver": "^7.8.3",
"semver": "^7.7.4",
"source-map-support": "^0.5.21",
"throttleit": "^2.1.0",
"winston": "^3.19.0",
"winston-syslog": "^2.7.1",
"winston-transport": "^4.9.0",
"ws": "^8.21.0",
"zigbee-herdsman": "10.4.0",
"zigbee-herdsman-converters": "26.63.0",
"ws": "^8.20.0",
"zigbee-herdsman": "10.0.7",
"zigbee-herdsman-converters": "26.39.1",
"zigbee2mqtt-frontend": "0.9.21",
"zigbee2mqtt-windfront": "2.12.0"
"zigbee2mqtt-windfront": "2.11.1"
},
"devDependencies": {
"@biomejs/biome": "^2.4.16",
"@biomejs/biome": "^2.4.12",
"@types/finalhandler": "^1.2.3",
"@types/humanize-duration": "^3.27.4",
"@types/js-yaml": "^4.0.9",
"@types/node": "^24.13.1",
"@types/node": "^24.12.2",
"@types/object-assign-deep": "^0.4.3",
"@types/readable-stream": "4.0.23",
"@types/serve-static": "^2.2.0",
"@types/ws": "8.18.1",
"@vitest/coverage-v8": "^3.1.1",
"tmp": "^0.2.7",
"tmp": "^0.2.5",
"typescript": "^6.0.3",
"vitest": "^3.1.1"
},
+173 -154
View File
@@ -5,15 +5,15 @@ settings:
excludeLinksFromLockfile: false
overrides:
zigbee-herdsman: 10.4.0
zigbee-herdsman: 10.0.7
importers:
.:
dependencies:
ajv:
specifier: ^8.20.0
version: 8.20.0
specifier: ^8.18.0
version: 8.18.0
bind-decorator:
specifier: ^1.0.11
version: 1.0.11
@@ -21,8 +21,8 @@ importers:
specifier: ^3.0.0
version: 3.0.0
express-static-gzip:
specifier: ^3.0.1
version: 3.0.1
specifier: ^3.0.0
version: 3.0.0
fast-deep-equal:
specifier: ^3.1.3
version: 3.1.3
@@ -33,8 +33,8 @@ importers:
specifier: ^3.33.2
version: 3.33.2
js-yaml:
specifier: ^4.2.0
version: 4.2.0
specifier: ^4.1.0
version: 4.1.1
json-stable-stringify-without-jsonify:
specifier: ^1.0.1
version: 1.0.1
@@ -51,8 +51,8 @@ importers:
specifier: ^6.1.3
version: 6.1.3
semver:
specifier: ^7.8.3
version: 7.8.3
specifier: ^7.7.4
version: 7.7.4
source-map-support:
specifier: ^0.5.21
version: 0.5.21
@@ -69,24 +69,24 @@ importers:
specifier: ^4.9.0
version: 4.9.0
ws:
specifier: ^8.21.0
version: 8.21.0
specifier: ^8.20.0
version: 8.20.0
zigbee-herdsman:
specifier: 10.4.0
version: 10.4.0
specifier: 10.0.7
version: 10.0.7
zigbee-herdsman-converters:
specifier: 26.63.0
version: 26.63.0
specifier: 26.39.1
version: 26.39.1
zigbee2mqtt-frontend:
specifier: 0.9.21
version: 0.9.21
zigbee2mqtt-windfront:
specifier: 2.12.0
version: 2.12.0
specifier: 2.11.1
version: 2.11.1
devDependencies:
'@biomejs/biome':
specifier: ^2.4.16
version: 2.4.16
specifier: ^2.4.12
version: 2.4.12
'@types/finalhandler':
specifier: ^1.2.3
version: 1.2.4
@@ -97,8 +97,8 @@ importers:
specifier: ^4.0.9
version: 4.0.9
'@types/node':
specifier: ^24.13.1
version: 24.13.1
specifier: ^24.12.2
version: 24.12.2
'@types/object-assign-deep':
specifier: ^0.4.3
version: 0.4.3
@@ -113,16 +113,16 @@ importers:
version: 8.18.1
'@vitest/coverage-v8':
specifier: ^3.1.1
version: 3.2.4(vitest@3.2.4(@types/node@24.13.1))
version: 3.2.4(vitest@3.2.4(@types/node@24.12.2))
tmp:
specifier: ^0.2.7
version: 0.2.7
specifier: ^0.2.5
version: 0.2.5
typescript:
specifier: ^6.0.3
version: 6.0.3
vitest:
specifier: ^3.1.1
version: 3.2.4(@types/node@24.13.1)
version: 3.2.4(@types/node@24.12.2)
optionalDependencies:
unix-dgram:
specifier: ^2.0.7
@@ -159,55 +159,55 @@ packages:
resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==}
engines: {node: '>=18'}
'@biomejs/biome@2.4.16':
resolution: {integrity: sha512-x9ajFh1zChVybCiM3TN6OD4phAqLgtPZjFrZF+aTMYCPjwBO+k529TX7PPsAqtGNLeV4UgzwQnowEgS7bGmzcA==}
'@biomejs/biome@2.4.12':
resolution: {integrity: sha512-Rro7adQl3NLq/zJCIL98eElXKI8eEiBtoeu5TbXF/U3qbjuSc7Jb5rjUbeHHcquDWeSf3HnGP7XI5qGrlRk/pA==}
engines: {node: '>=14.21.3'}
hasBin: true
'@biomejs/cli-darwin-arm64@2.4.16':
resolution: {integrity: sha512-wxPvu4XOA85YJk9ixSWUmq/QBHbid85BISbOAqqBM/5xQpPk9ayjk5375tOlSC0BeCwNSbPFafQBm+vBumXq0A==}
'@biomejs/cli-darwin-arm64@2.4.12':
resolution: {integrity: sha512-BnMU4Pc3ciEVteVpZ0BK33MLr7X57F5w1dwDLDn+/iy/yTrA4Q/N2yftidFtsA4vrDh0FMXDpacNV/Tl3fbmng==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [darwin]
'@biomejs/cli-darwin-x64@2.4.16':
resolution: {integrity: sha512-xFCqGPwYusQJp4N4NJLi1XJiZqjwFdjhT+KqtNy+Ug3qgfczqnTa6MSDvxJF6TkuDLoYJItMapz6tAf7kCekFw==}
'@biomejs/cli-darwin-x64@2.4.12':
resolution: {integrity: sha512-x9uJ0bI1rJsWICp3VH8w/5PnAVD3A7SqzDpbrfoUQX1QyWrK5jSU4fRLo/wSgGeplCivbxBRKmt5Xq4/nWvq8A==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [darwin]
'@biomejs/cli-linux-arm64-musl@2.4.16':
resolution: {integrity: sha512-oYxnW0ARfJkr72ezzF2OR8N/rtkgLUQeYtF8cFhVswbknHxtTcmzSsanVJP8yQKnGpGpc2ck6c5zLvHahL6Cbg==}
'@biomejs/cli-linux-arm64-musl@2.4.12':
resolution: {integrity: sha512-FhfpkAAlKL6kwvcVap0Hgp4AhZmtd3YImg0kK1jd7C/aSoh4SfsB2f++yG1rU0lr8Y5MCFJrcSkmssiL9Xnnig==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
'@biomejs/cli-linux-arm64@2.4.16':
resolution: {integrity: sha512-2kFb4//jxfZaP6D+Rj5VkHkxgyD9EoRAVBEQb8PKRv+s4NO2zYNJKXFaJmK1CmhufJOWEfpHKaRbOja7qjmdhQ==}
'@biomejs/cli-linux-arm64@2.4.12':
resolution: {integrity: sha512-tOwuCuZZtKi1jVzbk/5nXmIsziOB6yqN8c9r9QM0EJYPU6DpQWf11uBOSCfFKKM4H3d9ZoarvlgMfbcuD051Pw==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
'@biomejs/cli-linux-x64-musl@2.4.16':
resolution: {integrity: sha512-iHDS+MCM65DPqWGu+ECC3uoALyj2H7F4nVUPxIPjz/PIl94EUu+EDfGZDzFP+NY1EOPVt9NQvwFqq7HdMmowdg==}
'@biomejs/cli-linux-x64-musl@2.4.12':
resolution: {integrity: sha512-dwTIgZrGutzhkQCuvHynCkyW6hJxUuyZqKKO0YNfaS2GUoRO+tOvxXZqZB6SkWAOdfZTzwaw8IEdUnIkHKHoew==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
'@biomejs/cli-linux-x64@2.4.16':
resolution: {integrity: sha512-NbcBbi/nJqn5baae6wqRXdS7Gadf2uRpehSh6vMSYpG8OhkXl/Xg8aorWrJ+9VWqAT5ml90alLvorkpMW0nBwQ==}
'@biomejs/cli-linux-x64@2.4.12':
resolution: {integrity: sha512-8pFeAnLU9QdW9jCIslB/v82bI0lhBmz2ZAKc8pVMFPO0t0wAHsoEkrUQUbMkIorTRIjbqyNZHA3lEXavsPWYSw==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
'@biomejs/cli-win32-arm64@2.4.16':
resolution: {integrity: sha512-0rgImMsNb5v/chhkIFe3wu7PEFClS6RBAYUijGL9UsYN3PanSaoK24HSSuSJb1pYbYYVjzAyZTl3gtjJ84BM8A==}
'@biomejs/cli-win32-arm64@2.4.12':
resolution: {integrity: sha512-B0DLnx0vA9ya/3v7XyCaP+/lCpnbWbMOfUFFve+xb5OxyYvdHaS55YsSddr228Y+JAFk58agCuZTsqNiw2a6ig==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [win32]
'@biomejs/cli-win32-x64@2.4.16':
resolution: {integrity: sha512-Kp85jgoBHa05gix6UIRjfCDiUV3w/8VIdZ247VyyO2gEjaw12WEVhdIjlxp/AMzXxqxQwbxNTDVZ3Mwd2RG5rw==}
'@biomejs/cli-win32-x64@2.4.12':
resolution: {integrity: sha512-yMckRzTyZ83hkk8iDFWswqSdU8tvZxspJKnYNh7JZr/zhZNOlzH13k4ecboU6MurKExCe2HUkH75pGI/O2JwGA==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [win32]
@@ -219,8 +219,8 @@ packages:
'@dabh/diagnostics@2.0.8':
resolution: {integrity: sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==}
'@date-fns/tz@1.5.0':
resolution: {integrity: sha512-lwYN/vDPeNRULcepoE/LO2Pgx+7/RV+S9ARfbc9lr2DtGkOD7pAiruHvbR1RX3Qyf6ja47EWJDMsNK5vK08DJg==}
'@date-fns/tz@1.4.1':
resolution: {integrity: sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==}
'@esbuild/aix-ppc64@0.25.5':
resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==}
@@ -553,8 +553,8 @@ packages:
'@types/js-yaml@4.0.9':
resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==}
'@types/node@24.13.1':
resolution: {integrity: sha512-RSpUJGmvsJ1ZeBehQZFhIdpsz+bIpES0nIQXko4Ybq+N+kX6XvOq3Jo+iJ82FWLdblFq85AsMikd3m35jgezYg==}
'@types/node@24.12.2':
resolution: {integrity: sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==}
'@types/object-assign-deep@0.4.3':
resolution: {integrity: sha512-d9Gxaj5j1hzrxJ61EFEg13B4g4FgrT/DYtcDWFXPehR8DF2SUZbVMFtZIs8exkVRiqrqBpdTc/lUUZjncsPpMw==}
@@ -613,8 +613,8 @@ packages:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
engines: {node: '>=6.5'}
ajv@8.20.0:
resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==}
ajv@8.18.0:
resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==}
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
@@ -664,15 +664,15 @@ packages:
bl@6.1.6:
resolution: {integrity: sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==}
bonjour-service@1.4.0:
resolution: {integrity: sha512-fGQtj1qdR9vIKjFiWPQd52qIqwjaYqhcI40JEiDuvlZ86E7ZBPBwY9fPgHy9r2rYGIjiRfctNPYz6OQU73ww2w==}
bonjour-service@1.3.0:
resolution: {integrity: sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==}
brace-expansion@2.1.0:
resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==}
brace-expansion@2.0.2:
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
brace-expansion@5.0.6:
resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==}
engines: {node: 18 || 20 || >=22}
brace-expansion@5.0.2:
resolution: {integrity: sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==}
engines: {node: 20 || >=22}
broker-factory@3.1.14:
resolution: {integrity: sha512-L45k5HMbPIrMid0nTOZ/UPXG/c0aRuQKVrSDFIb1zOkvfiyHgYmIjc3cSiN1KwQIvRDOtKE0tfb3I9EZ3CmpQQ==}
@@ -745,6 +745,15 @@ packages:
supports-color:
optional: true
debug@4.4.1:
resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'}
@@ -815,8 +824,8 @@ packages:
resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==}
engines: {node: '>=12.0.0'}
express-static-gzip@3.0.1:
resolution: {integrity: sha512-LMeU/3YjFlFUa4vrPX+RoMMRW5mIpF4Iysgs6gX7A59WCY4BzyF3O28mBr4eMlWuW4DU9wVAVuVcfx29ln1N6g==}
express-static-gzip@3.0.0:
resolution: {integrity: sha512-36O10S0asHl3QojOBQQ0ZjXNtElmhgPS6erSUCCZymXkB/CK1mnGqOj4BTJN+FYRDIzVFnzo3wLFCZJvAk6rQQ==}
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
@@ -885,8 +894,8 @@ packages:
html-escaper@2.0.2:
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
http-errors@2.0.1:
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
http-errors@2.0.0:
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
engines: {node: '>= 0.8'}
humanize-duration@3.33.2:
@@ -952,8 +961,8 @@ packages:
js-tokens@9.0.1:
resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
js-yaml@4.2.0:
resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==}
js-yaml@4.1.1:
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
hasBin: true
json-schema-traverse@1.0.0:
@@ -999,9 +1008,9 @@ packages:
resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==}
engines: {node: '>= 0.6'}
mime-types@3.0.2:
resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==}
engines: {node: '>=18'}
mime-types@3.0.1:
resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==}
engines: {node: '>= 0.6'}
minimatch@10.2.0:
resolution: {integrity: sha512-ugkC31VaVg9cF0DFVoADH12k6061zNZkZON+aX8AWsR9GhPcErkcMBceb6znR8wLERM2AkkOxy2nWRLpT9Jq5w==}
@@ -1155,17 +1164,17 @@ packages:
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
semver@7.8.3:
resolution: {integrity: sha512-wnilbGyMxzbY7dNOl7jpKbLSjcfeweJWU5j4+u5qW+6/wuGD9KzIGOyZnQVSBM9E7DtWaaH3CyHkppYrKYoxwg==}
semver@7.7.4:
resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==}
engines: {node: '>=10'}
hasBin: true
send@1.2.1:
resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==}
send@1.2.0:
resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==}
engines: {node: '>= 18'}
serve-static@2.2.1:
resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==}
serve-static@2.2.0:
resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==}
engines: {node: '>= 18'}
setimmediate@1.0.5:
@@ -1221,6 +1230,10 @@ packages:
stackback@0.0.2:
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:
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
engines: {node: '>= 0.8'}
@@ -1293,8 +1306,8 @@ packages:
resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==}
engines: {node: '>=14.0.0'}
tmp@0.2.7:
resolution: {integrity: sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==}
tmp@0.2.5:
resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==}
engines: {node: '>=14.14'}
toidentifier@1.0.1:
@@ -1316,8 +1329,8 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
undici-types@7.18.2:
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
undici-types@7.16.0:
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
unix-dgram@2.0.6:
resolution: {integrity: sha512-AURroAsb73BZ6CdAyMrTk/hYKNj3DuYYEuOaB8bYMOHGKupRNScw90Q5C71tWJc3uE7dIeXRyuwN0xLLq3vDTg==}
@@ -1447,8 +1460,8 @@ packages:
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
engines: {node: '>=12'}
ws@8.21.0:
resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==}
ws@8.20.0:
resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
@@ -1459,12 +1472,12 @@ packages:
utf-8-validate:
optional: true
zigbee-herdsman-converters@26.63.0:
resolution: {integrity: sha512-5Hg4CNeu2PW0IZ+GsFtf8rqdMA8WrIMx+XZtpL4QWAOx5WpuzbyuFPseZKbs4iswtuf/zzPFZrGt07sx83srRg==}
zigbee-herdsman-converters@26.39.1:
resolution: {integrity: sha512-CopzHV9NNwNKypQv5TozRubgpUU+OVDCrzRgGwGRtmsqHmAysxF6QBWjguigRqzs5r/P5dDcigRUlDoDZC/+gA==}
engines: {node: '>=20.15.0'}
zigbee-herdsman@10.4.0:
resolution: {integrity: sha512-LO0LV9Qibgv4GuEoDjwZa1No18owetv0iQ1vZZ06rbv+SOUsg8uILBTCv5+S7PKYUYF4krVJH32x2qXYu02r5Q==}
zigbee-herdsman@10.0.7:
resolution: {integrity: sha512-QqKJaJryWf7IUuVNV6Dc1/qNPrs2dJANPJhRTG74nZBAsyIDn/vSyCP7g19fuEM7Cphr/0ZgCJZkHP2wKvNPMw==}
zigbee-on-host@0.2.4:
resolution: {integrity: sha512-NIG6CWp+Yfn7PjqEIRvenHqpwT1U7rSkyimnFOUIFpnmxQOJKrmcwWDfn6WjbU/YIYYWSQPlj+g13icu1xwlyg==}
@@ -1474,8 +1487,8 @@ packages:
resolution: {integrity: sha512-ClHYlG7g0v/tnIsD6kycNCi1ZEKIbQuU7lGJR3WqYcDmuqEMVcHDGbRiRwYRQcZGMxkqCf7guxNsXiKnC10+kg==}
engines: {node: '>=20.11'}
zigbee2mqtt-windfront@2.12.0:
resolution: {integrity: sha512-sDwgEFlmKMj9Sl9FLhfZPkYEd7185v4spwxYXZl7gwQR3xrxeWmnLHtpQ3DfTJd2Ns6V6hzC80Sp8EmB2z8KKg==}
zigbee2mqtt-windfront@2.11.1:
resolution: {integrity: sha512-2xM01JnuCWZlb1xYkIBVFAGJ6mkuAq/DRi8kfmB8KlA77LzHuX+f85pdgDotF9WGWb4+TqxmnpifYri1Nc3K2A==}
engines: {node: '>=22.12.0'}
snapshots:
@@ -1502,39 +1515,39 @@ snapshots:
'@bcoe/v8-coverage@1.0.2': {}
'@biomejs/biome@2.4.16':
'@biomejs/biome@2.4.12':
optionalDependencies:
'@biomejs/cli-darwin-arm64': 2.4.16
'@biomejs/cli-darwin-x64': 2.4.16
'@biomejs/cli-linux-arm64': 2.4.16
'@biomejs/cli-linux-arm64-musl': 2.4.16
'@biomejs/cli-linux-x64': 2.4.16
'@biomejs/cli-linux-x64-musl': 2.4.16
'@biomejs/cli-win32-arm64': 2.4.16
'@biomejs/cli-win32-x64': 2.4.16
'@biomejs/cli-darwin-arm64': 2.4.12
'@biomejs/cli-darwin-x64': 2.4.12
'@biomejs/cli-linux-arm64': 2.4.12
'@biomejs/cli-linux-arm64-musl': 2.4.12
'@biomejs/cli-linux-x64': 2.4.12
'@biomejs/cli-linux-x64-musl': 2.4.12
'@biomejs/cli-win32-arm64': 2.4.12
'@biomejs/cli-win32-x64': 2.4.12
'@biomejs/cli-darwin-arm64@2.4.16':
'@biomejs/cli-darwin-arm64@2.4.12':
optional: true
'@biomejs/cli-darwin-x64@2.4.16':
'@biomejs/cli-darwin-x64@2.4.12':
optional: true
'@biomejs/cli-linux-arm64-musl@2.4.16':
'@biomejs/cli-linux-arm64-musl@2.4.12':
optional: true
'@biomejs/cli-linux-arm64@2.4.16':
'@biomejs/cli-linux-arm64@2.4.12':
optional: true
'@biomejs/cli-linux-x64-musl@2.4.16':
'@biomejs/cli-linux-x64-musl@2.4.12':
optional: true
'@biomejs/cli-linux-x64@2.4.16':
'@biomejs/cli-linux-x64@2.4.12':
optional: true
'@biomejs/cli-win32-arm64@2.4.16':
'@biomejs/cli-win32-arm64@2.4.12':
optional: true
'@biomejs/cli-win32-x64@2.4.16':
'@biomejs/cli-win32-x64@2.4.12':
optional: true
'@colors/colors@1.6.0': {}
@@ -1545,7 +1558,7 @@ snapshots:
enabled: 2.0.0
kuler: 2.0.0
'@date-fns/tz@1.5.0': {}
'@date-fns/tz@1.4.1': {}
'@esbuild/aix-ppc64@0.25.5':
optional: true
@@ -1757,7 +1770,7 @@ snapshots:
'@types/finalhandler@1.2.4':
dependencies:
'@types/node': 24.13.1
'@types/node': 24.12.2
'@types/http-errors@2.0.5': {}
@@ -1765,33 +1778,33 @@ snapshots:
'@types/js-yaml@4.0.9': {}
'@types/node@24.13.1':
'@types/node@24.12.2':
dependencies:
undici-types: 7.18.2
undici-types: 7.16.0
'@types/object-assign-deep@0.4.3': {}
'@types/readable-stream@4.0.23':
dependencies:
'@types/node': 24.13.1
'@types/node': 24.12.2
'@types/serve-static@2.2.0':
dependencies:
'@types/http-errors': 2.0.5
'@types/node': 24.13.1
'@types/node': 24.12.2
'@types/triple-beam@1.3.5': {}
'@types/ws@8.18.1':
dependencies:
'@types/node': 24.13.1
'@types/node': 24.12.2
'@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/node@24.13.1))':
'@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/node@24.12.2))':
dependencies:
'@ampproject/remapping': 2.3.0
'@bcoe/v8-coverage': 1.0.2
ast-v8-to-istanbul: 0.3.3
debug: 4.4.3
debug: 4.4.1
istanbul-lib-coverage: 3.2.2
istanbul-lib-report: 3.0.1
istanbul-lib-source-maps: 5.0.6
@@ -1801,7 +1814,7 @@ snapshots:
std-env: 3.9.0
test-exclude: 7.0.1
tinyrainbow: 2.0.0
vitest: 3.2.4(@types/node@24.13.1)
vitest: 3.2.4(@types/node@24.12.2)
transitivePeerDependencies:
- supports-color
@@ -1813,13 +1826,13 @@ snapshots:
chai: 5.2.0
tinyrainbow: 2.0.0
'@vitest/mocker@3.2.4(vite@6.3.5(@types/node@24.13.1))':
'@vitest/mocker@3.2.4(vite@6.3.5(@types/node@24.12.2))':
dependencies:
'@vitest/spy': 3.2.4
estree-walker: 3.0.3
magic-string: 0.30.17
optionalDependencies:
vite: 6.3.5(@types/node@24.13.1)
vite: 6.3.5(@types/node@24.12.2)
'@vitest/pretty-format@3.2.4':
dependencies:
@@ -1851,7 +1864,7 @@ snapshots:
dependencies:
event-target-shim: 5.0.1
ajv@8.20.0:
ajv@8.18.0:
dependencies:
fast-deep-equal: 3.1.3
fast-uri: 3.1.0
@@ -1902,16 +1915,16 @@ snapshots:
inherits: 2.0.4
readable-stream: 4.7.0
bonjour-service@1.4.0:
bonjour-service@1.3.0:
dependencies:
fast-deep-equal: 3.1.3
multicast-dns: 7.2.5
brace-expansion@2.1.0:
brace-expansion@2.0.2:
dependencies:
balanced-match: 1.0.2
brace-expansion@5.0.6:
brace-expansion@5.0.2:
dependencies:
balanced-match: 4.0.2
@@ -1985,6 +1998,10 @@ snapshots:
dependencies:
ms: 2.1.3
debug@4.4.1:
dependencies:
ms: 2.1.3
debug@4.4.3:
dependencies:
ms: 2.1.3
@@ -2053,11 +2070,11 @@ snapshots:
expect-type@1.2.1: {}
express-static-gzip@3.0.1:
express-static-gzip@3.0.0:
dependencies:
mime-types: 3.0.2
mime-types: 3.0.1
parseurl: 1.3.3
serve-static: 2.2.1
serve-static: 2.2.0
transitivePeerDependencies:
- supports-color
@@ -2125,12 +2142,12 @@ snapshots:
html-escaper@2.0.2: {}
http-errors@2.0.1:
http-errors@2.0.0:
dependencies:
depd: 2.0.0
inherits: 2.0.4
setprototypeof: 1.2.0
statuses: 2.0.2
statuses: 2.0.1
toidentifier: 1.0.1
humanize-duration@3.33.2: {}
@@ -2166,7 +2183,7 @@ snapshots:
istanbul-lib-source-maps@5.0.6:
dependencies:
'@jridgewell/trace-mapping': 0.3.25
debug: 4.4.3
debug: 4.4.1
istanbul-lib-coverage: 3.2.2
transitivePeerDependencies:
- supports-color
@@ -2190,7 +2207,7 @@ snapshots:
js-tokens@9.0.1: {}
js-yaml@4.2.0:
js-yaml@4.1.1:
dependencies:
argparse: 2.0.1
@@ -2238,21 +2255,21 @@ snapshots:
make-dir@4.0.0:
dependencies:
semver: 7.8.3
semver: 7.7.4
mime-db@1.54.0: {}
mime-types@3.0.2:
mime-types@3.0.1:
dependencies:
mime-db: 1.54.0
minimatch@10.2.0:
dependencies:
brace-expansion: 5.0.6
brace-expansion: 5.0.2
minimatch@9.0.5:
dependencies:
brace-expansion: 2.1.0
brace-expansion: 2.0.2
minimist@1.2.8: {}
@@ -2283,7 +2300,7 @@ snapshots:
socks: 2.8.7
split2: 4.2.0
worker-timers: 8.0.31
ws: 8.21.0
ws: 8.20.0
transitivePeerDependencies:
- bufferutil
- supports-color
@@ -2427,17 +2444,17 @@ snapshots:
safer-buffer@2.1.2: {}
semver@7.8.3: {}
semver@7.7.4: {}
send@1.2.1:
send@1.2.0:
dependencies:
debug: 4.4.3
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
fresh: 2.0.0
http-errors: 2.0.1
mime-types: 3.0.2
http-errors: 2.0.0
mime-types: 3.0.1
ms: 2.1.3
on-finished: 2.4.1
range-parser: 1.2.1
@@ -2445,12 +2462,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
serve-static@2.2.1:
serve-static@2.2.0:
dependencies:
encodeurl: 2.0.0
escape-html: 1.0.3
parseurl: 1.3.3
send: 1.2.1
send: 1.2.0
transitivePeerDependencies:
- supports-color
@@ -2492,6 +2509,8 @@ snapshots:
stackback@0.0.2: {}
statuses@2.0.1: {}
statuses@2.0.2: {}
std-env@3.9.0: {}
@@ -2559,7 +2578,7 @@ snapshots:
tinyspy@4.0.3: {}
tmp@0.2.7: {}
tmp@0.2.5: {}
toidentifier@1.0.1: {}
@@ -2571,7 +2590,7 @@ snapshots:
typescript@6.0.3: {}
undici-types@7.18.2: {}
undici-types@7.16.0: {}
unix-dgram@2.0.6:
dependencies:
@@ -2587,13 +2606,13 @@ snapshots:
util-deprecate@1.0.2: {}
vite-node@3.2.4(@types/node@24.13.1):
vite-node@3.2.4(@types/node@24.12.2):
dependencies:
cac: 6.7.14
debug: 4.4.3
es-module-lexer: 1.7.0
pathe: 2.0.3
vite: 6.3.5(@types/node@24.13.1)
vite: 6.3.5(@types/node@24.12.2)
transitivePeerDependencies:
- '@types/node'
- jiti
@@ -2608,7 +2627,7 @@ snapshots:
- tsx
- yaml
vite@6.3.5(@types/node@24.13.1):
vite@6.3.5(@types/node@24.12.2):
dependencies:
esbuild: 0.25.5
fdir: 6.4.6(picomatch@4.0.2)
@@ -2617,21 +2636,21 @@ snapshots:
rollup: 4.44.0
tinyglobby: 0.2.14
optionalDependencies:
'@types/node': 24.13.1
'@types/node': 24.12.2
fsevents: 2.3.3
vitest@3.2.4(@types/node@24.13.1):
vitest@3.2.4(@types/node@24.12.2):
dependencies:
'@types/chai': 5.2.2
'@vitest/expect': 3.2.4
'@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@24.13.1))
'@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@24.12.2))
'@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4
'@vitest/spy': 3.2.4
'@vitest/utils': 3.2.4
chai: 5.2.0
debug: 4.4.3
debug: 4.4.1
expect-type: 1.2.1
magic-string: 0.30.17
pathe: 2.0.3
@@ -2642,11 +2661,11 @@ snapshots:
tinyglobby: 0.2.14
tinypool: 1.1.1
tinyrainbow: 2.0.0
vite: 6.3.5(@types/node@24.13.1)
vite-node: 3.2.4(@types/node@24.13.1)
vite: 6.3.5(@types/node@24.12.2)
vite-node: 3.2.4(@types/node@24.12.2)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 24.13.1
'@types/node': 24.12.2
transitivePeerDependencies:
- jiti
- less
@@ -2738,23 +2757,23 @@ snapshots:
string-width: 5.1.2
strip-ansi: 7.1.2
ws@8.21.0: {}
ws@8.20.0: {}
zigbee-herdsman-converters@26.63.0:
zigbee-herdsman-converters@26.39.1:
dependencies:
iconv-lite: 0.7.2
semver: 7.8.3
zigbee-herdsman: 10.4.0
semver: 7.7.4
zigbee-herdsman: 10.0.7
transitivePeerDependencies:
- supports-color
zigbee-herdsman@10.4.0:
zigbee-herdsman@10.0.7:
dependencies:
'@date-fns/tz': 1.5.0
'@date-fns/tz': 1.4.1
'@serialport/bindings-cpp': 13.0.1
'@serialport/parser-delimiter': 13.0.0
'@serialport/stream': 13.0.0
bonjour-service: 1.4.0
bonjour-service: 1.3.0
debounce: 3.0.0
fast-deep-equal: 3.1.3
slip: 1.0.2
@@ -2766,4 +2785,4 @@ snapshots:
zigbee2mqtt-frontend@0.9.21: {}
zigbee2mqtt-windfront@2.12.0: {}
zigbee2mqtt-windfront@2.11.1: {}
+23 -13
View File
@@ -1,12 +1,4 @@
import {execFileSync} from "node:child_process";
function grepTypeScriptFiles(needle, dir) {
try {
return execFileSync("grep", ["-r", "-F", "--include=*.ts", "--", needle, dir], {encoding: "utf8"});
} catch {
return undefined;
}
}
import {execSync} from "node:child_process";
async function checkDuplicateIssue(github, context, name) {
// Search for existing issues with the same `name`
@@ -71,7 +63,13 @@ export async function newDeviceSupport(github, _core, context, zhcDir) {
if (tuyaManufacturerNames.length > 0) {
for (const [fullName, partialName] of tuyaManufacturerNames) {
if (await checkDuplicateIssue(github, context, fullName)) return;
const fullMatch = grepTypeScriptFiles(fullName, zhcDir);
const fullMatch = (() => {
try {
return execSync(`grep -r --include="*.ts" "${fullName}" "${zhcDir}"`, {encoding: "utf8"});
} catch {
return undefined;
}
})();
console.log(`Checking full match for '${fullName}', result: '${fullMatch}'`);
if (fullMatch) {
@@ -96,7 +94,13 @@ If you need help with the process, feel free to ask here and we'll be happy to a
return;
}
const partialMatch = grepTypeScriptFiles(partialName, zhcDir);
const partialMatch = (() => {
try {
return execSync(`grep -r --include="*.ts" "${partialName}" "${zhcDir}"`, {encoding: "utf8"});
} catch {
return undefined;
}
})();
console.log(`Checking partial match for '${partialName}', result: '${partialMatch}'`);
if (partialMatch) {
@@ -125,8 +129,14 @@ Let us know if it works so we can support this device out-of-the-box!`,
if (zigbeeModels.length > 0) {
for (const zigbeeModel of zigbeeModels) {
if (await checkDuplicateIssue(github, context, zigbeeModel)) return;
const fullMatch = grepTypeScriptFiles(`"${zigbeeModel}"`, zhcDir);
if (await checkDuplicateIssue(github, context, fullName)) return;
const fullMatch = (() => {
try {
return execSync(`grep -r --include="*.ts" '"${zigbeeModel}"' "${zhcDir}"`, {encoding: "utf8"});
} catch {
return undefined;
}
})();
if (fullMatch) {
await github.rest.issues.createComment({
-2
View File
@@ -144,7 +144,6 @@ describe("Extension: Bridge", () => {
output: "json",
pan_id: 6754,
timestamp_format: "YYYY-MM-DD HH:mm:ss",
enable_external_js: true,
},
blocklist: [],
device_options: {},
@@ -750,7 +749,6 @@ describe("Extension: Bridge", () => {
"fireplace",
"colorloop",
"sunset",
"sunrise",
"sparkle",
"opal",
"glisten",
@@ -488,25 +488,6 @@ describe("Extension: ExternalConverters", () => {
expect(mockMQTTPublishAsync).toHaveBeenCalledWith("zigbee2mqtt/bridge/converters", stringify([]), {retain: true});
});
it("returns error on invalid name", async () => {
const converterName = "foo1";
await resetExtension();
for (const mock of mocksClear) mock.mockClear();
await (controller.getExtension("ExternalConverters")! as ExternalConverters).onMQTTMessage({
topic: "zigbee2mqtt/bridge/request/converter/save",
message: {name: converterName, code: "a"},
});
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
"zigbee2mqtt/bridge/response/converter/save",
expect.stringContaining(`JavaScript file must have '.mjs', '.js' or '.cjs' extension`),
{},
);
expect(writeFileSyncSpy).toHaveBeenCalledTimes(0);
});
it("returns error on invalid code", async () => {
const converterName = "foo1.js";
const converterCode = "definetly not a correct javascript code";
@@ -628,15 +609,4 @@ describe("Extension: ExternalConverters", () => {
);
});
});
it("doesn't add extension when external JS disabled", async () => {
settings.set(["advanced", "enable_external_js"], false);
controller = new Controller(vi.fn(), vi.fn());
await controller.start();
await flushPromises();
expect(controller.getExtension("ExternalConverters")).toBeUndefined();
});
});
@@ -299,25 +299,6 @@ describe("Extension: ExternalExtensions", () => {
expect(mockMQTTPublishAsync).toHaveBeenCalledWith("zigbee2mqtt/bridge/extensions", stringify([]), {retain: true});
});
it("returns error on invalid name", async () => {
const extensionName = "foo1";
await resetExtension();
for (const mock of mocksClear) mock.mockClear();
await (controller.getExtension("ExternalExtensions")! as ExternalExtensions).onMQTTMessage({
topic: "zigbee2mqtt/bridge/request/extension/save",
message: {name: extensionName, code: "a"},
});
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
"zigbee2mqtt/bridge/response/extension/save",
expect.stringContaining(`JavaScript file must have '.mjs', '.js' or '.cjs' extension`),
{},
);
expect(writeFileSyncSpy).toHaveBeenCalledTimes(0);
});
it("returns error on invalid code", async () => {
const extensionName = "foo1.js";
const extensionCode = "definetly not a correct javascript code";
@@ -384,15 +365,4 @@ describe("Extension: ExternalExtensions", () => {
);
});
});
it("doesn't add extension when external JS disabled", async () => {
settings.set(["advanced", "enable_external_js"], false);
controller = new Controller(vi.fn(), vi.fn());
await controller.start();
await flushPromises();
expect(controller.getExtension("ExternalExtensions")).toBeUndefined();
});
});
+58 -60
View File
@@ -165,7 +165,6 @@ describe("Extension: HomeAssistant", () => {
"fireplace",
"colorloop",
"sunset",
"sunrise",
"sparkle",
"opal",
"glisten",
@@ -239,7 +238,7 @@ describe("Extension: HomeAssistant", () => {
unique_id: "9_switch_zigbee2mqtt",
group: ["0x0017880104e45542_switch_right_zigbee2mqtt"],
origin: origin,
value_template: '{{ value_json["state"] }}',
value_template: "{{ value_json.state }}",
};
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
@@ -252,7 +251,7 @@ describe("Extension: HomeAssistant", () => {
unit_of_measurement: "°C",
device_class: "temperature",
state_class: "measurement",
value_template: '{{ value_json["temperature"] }}',
value_template: "{{ value_json.temperature }}",
state_topic: "zigbee2mqtt/weather_sensor",
object_id: "weather_sensor_temperature",
default_entity_id: "sensor.weather_sensor_temperature",
@@ -279,7 +278,7 @@ describe("Extension: HomeAssistant", () => {
unit_of_measurement: "%",
device_class: "humidity",
state_class: "measurement",
value_template: '{{ value_json["humidity"] }}',
value_template: "{{ value_json.humidity }}",
state_topic: "zigbee2mqtt/weather_sensor",
object_id: "weather_sensor_humidity",
default_entity_id: "sensor.weather_sensor_humidity",
@@ -306,7 +305,7 @@ describe("Extension: HomeAssistant", () => {
unit_of_measurement: "hPa",
device_class: "atmospheric_pressure",
state_class: "measurement",
value_template: '{{ value_json["pressure"] }}',
value_template: "{{ value_json.pressure }}",
state_topic: "zigbee2mqtt/weather_sensor",
object_id: "weather_sensor_pressure",
default_entity_id: "sensor.weather_sensor_pressure",
@@ -333,7 +332,7 @@ describe("Extension: HomeAssistant", () => {
unit_of_measurement: "%",
device_class: "battery",
state_class: "measurement",
value_template: '{{ value_json["battery"] }}',
value_template: "{{ value_json.battery }}",
state_topic: "zigbee2mqtt/weather_sensor",
object_id: "weather_sensor_battery",
default_entity_id: "sensor.weather_sensor_battery",
@@ -363,7 +362,7 @@ describe("Extension: HomeAssistant", () => {
entity_category: "diagnostic",
unit_of_measurement: "lqi",
state_class: "measurement",
value_template: '{{ value_json["linkquality"] }}',
value_template: "{{ value_json.linkquality }}",
state_topic: "zigbee2mqtt/weather_sensor",
name: "Linkquality",
object_id: "weather_sensor_linkquality",
@@ -405,7 +404,7 @@ describe("Extension: HomeAssistant", () => {
default_entity_id: "switch.wall_switch_double_left",
unique_id: "0x0017880104e45542_switch_left_zigbee2mqtt",
origin: origin,
value_template: '{{ value_json["state_left"] }}',
value_template: "{{ value_json.state_left }}",
};
expect(mockMQTTPublishAsync).toHaveBeenCalledWith("homeassistant/switch/0x0017880104e45542/switch_left/config", stringify(payload), {
@@ -432,7 +431,7 @@ describe("Extension: HomeAssistant", () => {
default_entity_id: "switch.wall_switch_double_right",
unique_id: "0x0017880104e45542_switch_right_zigbee2mqtt",
origin: origin,
value_template: '{{ value_json["state_right"] }}',
value_template: "{{ value_json.state_right }}",
};
expect(mockMQTTPublishAsync).toHaveBeenCalledWith("homeassistant/switch/0x0017880104e45542/switch_right/config", stringify(payload), {
@@ -535,7 +534,7 @@ describe("Extension: HomeAssistant", () => {
unit_of_measurement: "%",
device_class: "humidity",
state_class: "measurement",
value_template: '{{ value_json["humidity"] }}',
value_template: "{{ value_json.humidity }}",
state_topic: "zigbee2mqtt/weather_sensor",
object_id: "weather_sensor_humidity",
default_entity_id: "sensor.weather_sensor_humidity",
@@ -607,7 +606,7 @@ describe("Extension: HomeAssistant", () => {
device_class: "temperature",
state_class: "measurement",
enabled_by_default: true,
value_template: '{{ value_json["temperature"] }}',
value_template: "{{ value_json.temperature }}",
state_topic: "zigbee2mqtt/weather_sensor",
object_id: "weather_sensor_temperature",
default_entity_id: "sensor.weather_sensor_temperature",
@@ -633,7 +632,7 @@ describe("Extension: HomeAssistant", () => {
unit_of_measurement: "%",
device_class: "humidity",
state_class: "measurement",
value_template: '{{ value_json["humidity"] }}',
value_template: "{{ value_json.humidity }}",
state_topic: "zigbee2mqtt/weather_sensor",
object_id: "weather_sensor_humidity",
default_entity_id: "sensor.weather_sensor_humidity",
@@ -660,7 +659,7 @@ describe("Extension: HomeAssistant", () => {
unit_of_measurement: "hPa",
device_class: "atmospheric_pressure",
state_class: "measurement",
value_template: '{{ value_json["pressure"] }}',
value_template: "{{ value_json.pressure }}",
state_topic: "zigbee2mqtt/weather_sensor",
enabled_by_default: true,
object_id: "weather_sensor_pressure",
@@ -718,7 +717,7 @@ describe("Extension: HomeAssistant", () => {
unit_of_measurement: "°C",
device_class: "temperature",
state_class: "measurement",
value_template: '{{ value_json["temperature"] }}',
value_template: "{{ value_json.temperature }}",
state_topic: "zigbee2mqtt/weather_sensor",
enabled_by_default: true,
object_id: "weather_sensor_temperature",
@@ -748,7 +747,7 @@ describe("Extension: HomeAssistant", () => {
unit_of_measurement: "%",
device_class: "humidity",
state_class: "measurement",
value_template: '{{ value_json["humidity"] }}',
value_template: "{{ value_json.humidity }}",
state_topic: "zigbee2mqtt/weather_sensor",
enabled_by_default: true,
device: {
@@ -791,7 +790,7 @@ describe("Extension: HomeAssistant", () => {
unit_of_measurement: "°C",
device_class: "temperature",
state_class: "measurement",
value_template: '{{ value_json["temperature"] }}',
value_template: "{{ value_json.temperature }}",
state_topic: "zigbee2mqtt/weather_sensor",
object_id: "weather_sensor_temperature",
default_entity_id: "sensor.weather_sensor_temperature",
@@ -818,7 +817,7 @@ describe("Extension: HomeAssistant", () => {
unit_of_measurement: "%",
device_class: "humidity",
state_class: "measurement",
value_template: '{{ value_json["humidity"] }}',
value_template: "{{ value_json.humidity }}",
state_topic: "zigbee2mqtt/weather_sensor",
object_id: "weather_sensor_humidity",
default_entity_id: "sensor.weather_sensor_humidity",
@@ -881,7 +880,7 @@ describe("Extension: HomeAssistant", () => {
default_entity_id: "light.my_switch",
unique_id: "0x0017880104e45541_light_zigbee2mqtt",
origin: origin,
value_template: '{{ value_json["state"] }}',
value_template: "{{ value_json.state }}",
};
expect(mockMQTTPublishAsync).toHaveBeenCalledWith("homeassistant/light/0x0017880104e45541/light/config", stringify(payload), {
@@ -927,11 +926,11 @@ describe("Extension: HomeAssistant", () => {
command_topic: "zigbee2mqtt/fan/set/fan_state",
percentage_state_topic: "zigbee2mqtt/fan",
percentage_command_topic: "zigbee2mqtt/fan/set/fan_mode",
percentage_value_template: "{{ {'off':0, 'low':1, 'medium':2, 'high':3, 'on':4}[value_json[\"fan_mode\"]] | default('None') }}",
percentage_value_template: "{{ {'off':0, 'low':1, 'medium':2, 'high':3, 'on':4}[value_json.fan_mode] | default('None') }}",
percentage_command_template: "{{ {0:'off', 1:'low', 2:'medium', 3:'high', 4:'on'}[value] | default('') }}",
preset_mode_state_topic: "zigbee2mqtt/fan",
preset_mode_command_topic: "zigbee2mqtt/fan/set/fan_mode",
preset_mode_value_template: "{{ value_json[\"fan_mode\"] if value_json[\"fan_mode\"] in ['smart'] else 'None' | default('None') }}",
preset_mode_value_template: "{{ value_json.fan_mode if value_json.fan_mode in ['smart'] else 'None' | default('None') }}",
preset_modes: ["smart"],
speed_range_min: 1,
speed_range_max: 4,
@@ -1003,7 +1002,7 @@ describe("Extension: HomeAssistant", () => {
command_topic: "zigbee2mqtt/fanbee/set/state",
percentage_state_topic: "zigbee2mqtt/fanbee",
percentage_command_topic: "zigbee2mqtt/fanbee/set/speed",
percentage_value_template: "{{ value_json[\"speed\"] | default('None') }}",
percentage_value_template: "{{ value_json.speed | default('None') }}",
percentage_command_template: "{{ value | default('') }}",
speed_range_min: 1,
speed_range_max: 254,
@@ -1036,7 +1035,7 @@ describe("Extension: HomeAssistant", () => {
it("Should discover thermostat devices", () => {
const payload = {
action_template:
"{% set values = {None:None,'idle':'idle','heat':'heating','cool':'cooling','fan_only':'fan'} %}{{ values[value_json[\"running_state\"]] }}",
"{% set values = {None:None,'idle':'idle','heat':'heating','cool':'cooling','fan_only':'fan'} %}{{ values[value_json.running_state] }}",
action_topic: "zigbee2mqtt/TS0601_thermostat",
availability: [
{
@@ -1044,7 +1043,7 @@ describe("Extension: HomeAssistant", () => {
value_template: "{{ value_json.state }}",
},
],
current_temperature_template: '{{ value_json["local_temperature"] }}',
current_temperature_template: "{{ value_json.local_temperature }}",
current_temperature_topic: "zigbee2mqtt/TS0601_thermostat",
device: {
identifiers: ["zigbee2mqtt_0x0017882104a44559"],
@@ -1056,18 +1055,18 @@ describe("Extension: HomeAssistant", () => {
},
preset_mode_command_topic: "zigbee2mqtt/TS0601_thermostat/set/preset",
preset_modes: ["schedule", "manual", "boost", "complex", "comfort", "eco", "away"],
preset_mode_value_template: '{{ value_json["preset"] }}',
preset_mode_value_template: "{{ value_json.preset }}",
preset_mode_state_topic: "zigbee2mqtt/TS0601_thermostat",
max_temp: "35",
min_temp: "5",
mode_command_topic: "zigbee2mqtt/TS0601_thermostat/set/system_mode",
mode_state_template: '{{ value_json["system_mode"] }}',
mode_state_template: "{{ value_json.system_mode }}",
mode_state_topic: "zigbee2mqtt/TS0601_thermostat",
modes: ["heat", "auto", "off"],
name: null,
temp_step: 0.5,
temperature_command_topic: "zigbee2mqtt/TS0601_thermostat/set/current_heating_setpoint",
temperature_state_template: '{{ value_json["current_heating_setpoint"] }}',
temperature_state_template: "{{ value_json.current_heating_setpoint }}",
temperature_state_topic: "zigbee2mqtt/TS0601_thermostat",
temperature_unit: "C",
object_id: "ts0601_thermostat",
@@ -1107,7 +1106,7 @@ describe("Extension: HomeAssistant", () => {
state_topic: "zigbee2mqtt/thermostat",
unique_id: "0x0017880104e45550_pi_heating_demand_zigbee2mqtt",
unit_of_measurement: "%",
value_template: '{{ value_json["pi_heating_demand"] }}',
value_template: "{{ value_json.pi_heating_demand }}",
};
expect(mockMQTTPublishAsync).toHaveBeenCalledWith("homeassistant/sensor/0x0017880104e45550/pi_heating_demand/config", stringify(payload), {
@@ -1144,7 +1143,7 @@ describe("Extension: HomeAssistant", () => {
state_topic: "zigbee2mqtt/bosch_radiator",
unique_id: "0x18fc2600000d7ae2_pi_heating_demand_zigbee2mqtt",
unit_of_measurement: "%",
value_template: '{{ value_json["pi_heating_demand"] }}',
value_template: "{{ value_json.pi_heating_demand }}",
};
expect(mockMQTTPublishAsync).toHaveBeenCalledWith("homeassistant/number/0x18fc2600000d7ae2/pi_heating_demand/config", stringify(payload), {
@@ -1156,10 +1155,10 @@ describe("Extension: HomeAssistant", () => {
it("Should discover Bosch BTH-RA with a compatibility mapping", () => {
const payload = {
action_template:
"{% set values = {None:None,'idle':'idle','heat':'heating','cool':'cooling','fan_only':'fan'} %}{{ values[value_json[\"running_state\"]] }}",
"{% set values = {None:None,'idle':'idle','heat':'heating','cool':'cooling','fan_only':'fan'} %}{{ values[value_json.running_state] }}",
action_topic: "zigbee2mqtt/bosch_radiator",
availability: [{topic: "zigbee2mqtt/bridge/state", value_template: "{{ value_json.state }}"}],
current_temperature_template: '{{ value_json["local_temperature"] }}',
current_temperature_template: "{{ value_json.local_temperature }}",
current_temperature_topic: "zigbee2mqtt/bosch_radiator",
device: {
identifiers: ["zigbee2mqtt_0x18fc2600000d7ae2"],
@@ -1184,7 +1183,7 @@ describe("Extension: HomeAssistant", () => {
origin: origin,
temp_step: 0.5,
temperature_command_topic: "zigbee2mqtt/bosch_radiator/set/occupied_heating_setpoint",
temperature_state_template: '{{ value_json["occupied_heating_setpoint"] }}',
temperature_state_template: "{{ value_json.occupied_heating_setpoint }}",
temperature_state_topic: "zigbee2mqtt/bosch_radiator",
temperature_unit: "C",
unique_id: "0x18fc2600000d7ae2_climate_zigbee2mqtt",
@@ -1211,10 +1210,10 @@ describe("Extension: HomeAssistant", () => {
const payload = {
action_template:
"{% set values = {None:None,'idle':'idle','heat':'heating','cool':'cooling','fan_only':'fan'} %}{{ values[value_json[\"running_state\"]] }}",
"{% set values = {None:None,'idle':'idle','heat':'heating','cool':'cooling','fan_only':'fan'} %}{{ values[value_json.running_state] }}",
action_topic: "zigbee2mqtt/bosch_radiator",
availability: [{topic: "zigbee2mqtt/bridge/state", value_template: "{{ value_json.state }}"}],
current_temperature_template: '{{ value_json["local_temperature"] }}',
current_temperature_template: "{{ value_json.local_temperature }}",
current_temperature_topic: "zigbee2mqtt/bosch_radiator",
device: {
identifiers: ["zigbee2mqtt_0x18fc2600000d7ae2"],
@@ -1228,7 +1227,7 @@ describe("Extension: HomeAssistant", () => {
max_temp: "30",
min_temp: "5",
mode_command_topic: "zigbee2mqtt/bosch_radiator/set/system_mode",
mode_state_template: '{{ value_json["system_mode"] }}',
mode_state_template: "{{ value_json.system_mode }}",
mode_state_topic: "zigbee2mqtt/bosch_radiator",
modes: ["heat"],
name: null,
@@ -1237,7 +1236,7 @@ describe("Extension: HomeAssistant", () => {
origin: origin,
temp_step: 0.5,
temperature_command_topic: "zigbee2mqtt/bosch_radiator/set/occupied_heating_setpoint",
temperature_state_template: '{{ value_json["occupied_heating_setpoint"] }}',
temperature_state_template: "{{ value_json.occupied_heating_setpoint }}",
temperature_state_topic: "zigbee2mqtt/bosch_radiator",
temperature_unit: "C",
unique_id: "0x18fc2600000d7ae2_climate_zigbee2mqtt",
@@ -1255,12 +1254,12 @@ describe("Extension: HomeAssistant", () => {
it("Should discover Bosch BTH-RM230Z with a current_humidity attribute", () => {
const payload = {
action_template:
"{% set values = {None:None,'idle':'idle','heat':'heating','cool':'cooling','fan_only':'fan'} %}{{ values[value_json[\"running_state\"]] }}",
"{% set values = {None:None,'idle':'idle','heat':'heating','cool':'cooling','fan_only':'fan'} %}{{ values[value_json.running_state] }}",
action_topic: "zigbee2mqtt/bosch_rm230z",
availability: [{topic: "zigbee2mqtt/bridge/state", value_template: "{{ value_json.state }}"}],
current_humidity_template: '{{ value_json["humidity"] }}',
current_humidity_template: "{{ value_json.humidity }}",
current_humidity_topic: "zigbee2mqtt/bosch_rm230z",
current_temperature_template: '{{ value_json["local_temperature"] }}',
current_temperature_template: "{{ value_json.local_temperature }}",
current_temperature_topic: "zigbee2mqtt/bosch_rm230z",
default_entity_id: "climate.bosch_rm230z",
device: {
@@ -1285,10 +1284,10 @@ describe("Extension: HomeAssistant", () => {
origin,
temp_step: 0.5,
temperature_high_command_topic: "zigbee2mqtt/bosch_rm230z/set/occupied_cooling_setpoint",
temperature_high_state_template: '{{ value_json["occupied_cooling_setpoint"] }}',
temperature_high_state_template: "{{ value_json.occupied_cooling_setpoint }}",
temperature_high_state_topic: "zigbee2mqtt/bosch_rm230z",
temperature_low_command_topic: "zigbee2mqtt/bosch_rm230z/set/occupied_heating_setpoint",
temperature_low_state_template: '{{ value_json["occupied_heating_setpoint"] }}',
temperature_low_state_template: "{{ value_json.occupied_heating_setpoint }}",
temperature_low_state_topic: "zigbee2mqtt/bosch_rm230z",
temperature_unit: "C",
unique_id: "0x18fc2600000d7ae3_climate_zigbee2mqtt",
@@ -1319,7 +1318,7 @@ describe("Extension: HomeAssistant", () => {
state_topic: "zigbee2mqtt/bosch_rm230z",
unique_id: "0x18fc2600000d7ae3_local_temperature_zigbee2mqtt",
unit_of_measurement: "°C",
value_template: '{{ value_json["local_temperature"] }}',
value_template: "{{ value_json.local_temperature }}",
};
expect(mockMQTTPublishAsync).toHaveBeenCalledWith("homeassistant/sensor/0x18fc2600000d7ae3/local_temperature/config", stringify(payload), {
@@ -1336,9 +1335,9 @@ describe("Extension: HomeAssistant", () => {
position_topic: "zigbee2mqtt/smart vent",
set_position_topic: "zigbee2mqtt/smart vent/set",
set_position_template: '{ "position": {{ position }} }',
position_template: '{{ value_json["position"] }}',
position_template: "{{ value_json.position }}",
state_topic: "zigbee2mqtt/smart vent",
value_template: '{{ value_json["state"] }}',
value_template: "{{ value_json.state }}",
state_open: "OPEN",
state_closed: "CLOSE",
state_stopped: "STOP",
@@ -1375,7 +1374,7 @@ describe("Extension: HomeAssistant", () => {
via_device: "zigbee2mqtt_bridge_0x00124b00120144ae",
},
name: "L6",
position_template: '{{ value_json["position"] }}',
position_template: "{{ value_json.position }}",
position_topic: "zigbee2mqtt/zigfred_plus/l6",
set_position_template: '{ "position_l6": {{ position }} }',
set_position_topic: "zigbee2mqtt/zigfred_plus/l6/set",
@@ -1384,13 +1383,13 @@ describe("Extension: HomeAssistant", () => {
state_open: "OPEN",
state_topic: "zigbee2mqtt/zigfred_plus/l6",
tilt_command_topic: "zigbee2mqtt/zigfred_plus/l6/set/tilt",
tilt_status_template: '{{ value_json["tilt"] }}',
tilt_status_template: "{{ value_json.tilt }}",
tilt_status_topic: "zigbee2mqtt/zigfred_plus/l6",
object_id: "zigfred_plus_l6",
default_entity_id: "cover.zigfred_plus_l6",
unique_id: "0xf4ce368a38be56a1_cover_l6_zigbee2mqtt",
origin: origin,
value_template: '{{ value_json["state"] }}',
value_template: "{{ value_json.state }}",
};
expect(mockMQTTPublishAsync).toHaveBeenCalledWith("homeassistant/cover/0xf4ce368a38be56a1/cover_l6/config", stringify(payload), {
@@ -1420,7 +1419,7 @@ describe("Extension: HomeAssistant", () => {
object_id: "0xa4c138018cf95021_left",
default_entity_id: "cover.0xa4c138018cf95021_left",
origin: origin,
position_template: '{{ value_json["position"] }}',
position_template: "{{ value_json.position }}",
position_topic: "zigbee2mqtt/0xa4c138018cf95021/left",
set_position_template: '{ "position_left": {{ position }} }',
set_position_topic: "zigbee2mqtt/0xa4c138018cf95021/left/set",
@@ -1429,7 +1428,7 @@ describe("Extension: HomeAssistant", () => {
state_stopped: "STOP",
state_topic: "zigbee2mqtt/0xa4c138018cf95021/left",
unique_id: "0xa4c138018cf95021_cover_left_zigbee2mqtt",
value_template: '{% if "moving" in value_json and value_json["moving"] %} {{ value_json["moving"] }} {% else %} STOP {% endif %}',
value_template: '{% if "moving" in value_json and value_json.moving %} {{ value_json.moving }} {% else %} STOP {% endif %}',
};
const payload_right = {
availability: [
@@ -1451,7 +1450,7 @@ describe("Extension: HomeAssistant", () => {
object_id: "0xa4c138018cf95021_right",
default_entity_id: "cover.0xa4c138018cf95021_right",
origin: origin,
position_template: '{{ value_json["position"] }}',
position_template: "{{ value_json.position }}",
position_topic: "zigbee2mqtt/0xa4c138018cf95021/right",
set_position_template: '{ "position_right": {{ position }} }',
set_position_topic: "zigbee2mqtt/0xa4c138018cf95021/right/set",
@@ -1460,9 +1459,11 @@ describe("Extension: HomeAssistant", () => {
state_stopped: "STOP",
state_topic: "zigbee2mqtt/0xa4c138018cf95021/right",
unique_id: "0xa4c138018cf95021_cover_right_zigbee2mqtt",
value_template: '{% if "moving" in value_json and value_json["moving"] %} {{ value_json["moving"] }} {% else %} STOP {% endif %}',
value_template: '{% if "moving" in value_json and value_json.moving %} {{ value_json.moving }} {% else %} STOP {% endif %}',
};
console.log(mockMQTTPublishAsync.mock.calls.find((c) => c[0] === "homeassistant/cover/0xa4c138018cf95021/cover_left/config"));
expect(mockMQTTPublishAsync).toHaveBeenCalledWith("homeassistant/cover/0xa4c138018cf95021/cover_left/config", stringify(payload_left), {
retain: true,
qos: 1,
@@ -1481,7 +1482,7 @@ describe("Extension: HomeAssistant", () => {
unit_of_measurement: "°C",
device_class: "temperature",
state_class: "measurement",
value_template: '{{ value_json["temperature"] }}',
value_template: "{{ value_json.temperature }}",
state_topic: "zigbee2mqtt/weather_sensor",
enabled_by_default: true,
object_id: "weather_sensor_temperature",
@@ -1667,7 +1668,7 @@ describe("Extension: HomeAssistant", () => {
device_class: "temperature",
enabled_by_default: true,
state_class: "measurement",
value_template: '{{ value_json["temperature"] }}',
value_template: "{{ value_json.temperature }}",
state_topic: "zigbee2mqtt/weather_sensor",
object_id: "weather_sensor_temperature",
default_entity_id: "sensor.weather_sensor_temperature",
@@ -1828,7 +1829,7 @@ describe("Extension: HomeAssistant", () => {
device_class: "temperature",
enabled_by_default: true,
state_class: "measurement",
value_template: '{{ value_json["temperature"] }}',
value_template: "{{ value_json.temperature }}",
state_topic: "zigbee2mqtt/weather_sensor",
object_id: "weather_sensor_temperature",
default_entity_id: "sensor.weather_sensor_temperature",
@@ -1898,7 +1899,7 @@ describe("Extension: HomeAssistant", () => {
device_class: "temperature",
state_class: "measurement",
enabled_by_default: true,
value_template: '{{ value_json["temperature"] }}',
value_template: "{{ value_json.temperature }}",
state_topic: "zigbee2mqtt/weather_sensor_renamed",
object_id: "weather_sensor_renamed_temperature",
default_entity_id: "sensor.weather_sensor_renamed_temperature",
@@ -1983,7 +1984,6 @@ describe("Extension: HomeAssistant", () => {
"fireplace",
"colorloop",
"sunset",
"sunrise",
"sparkle",
"opal",
"glisten",
@@ -2032,7 +2032,7 @@ describe("Extension: HomeAssistant", () => {
device_class: "temperature",
state_class: "measurement",
enabled_by_default: true,
value_template: '{{ value_json["temperature"] }}',
value_template: "{{ value_json.temperature }}",
state_topic: "zigbee2mqtt/weather_sensor_renamed",
object_id: "weather_sensor_renamed_temperature",
default_entity_id: "sensor.weather_sensor_renamed_temperature",
@@ -2463,7 +2463,6 @@ describe("Extension: HomeAssistant", () => {
"fireplace",
"colorloop",
"sunset",
"sunrise",
"sparkle",
"opal",
"glisten",
@@ -2519,7 +2518,6 @@ describe("Extension: HomeAssistant", () => {
"fireplace",
"colorloop",
"sunset",
"sunrise",
"sparkle",
"opal",
"glisten",
@@ -2642,7 +2640,7 @@ describe("Extension: HomeAssistant", () => {
device_class: "temperature",
state_class: "measurement",
enabled_by_default: true,
value_template: '{{ value_json["temperature"] }}',
value_template: "{{ value_json.temperature }}",
state_topic: "zigbee2mqtt/weather_sensor",
object_id: "weather_sensor_temperature",
default_entity_id: "sensor.weather_sensor_temperature",
@@ -3009,7 +3007,7 @@ describe("Extension: HomeAssistant", () => {
origin: origin,
state_topic: "zigbee2mqtt/0x18fc26000000cafe",
unique_id: "0x18fc26000000cafe_device_mode_zigbee2mqtt",
value_template: '{{ value_json["device_mode"] }}',
value_template: "{{ value_json.device_mode }}",
};
expect(mockMQTTPublishAsync).toHaveBeenCalledWith("homeassistant/select/0x18fc26000000cafe/device_mode/config", stringify(payload), {
retain: true,
-65
View File
@@ -546,71 +546,6 @@ describe("Extension: OTAUpdate", () => {
expect(existsSync(saveFilePath)).toStrictEqual(false);
});
it("aborts running OTA", async () => {
let timer: NodeJS.Timeout | undefined;
devices.bulb.updateOta.mockImplementationOnce(
async (_source, _requestPayload, _requestTsn, _extraMetas, onProgress, _dataSettings, _endpoint) => {
onProgress(0, 36000.5678);
onProgress(10, 3600.2123);
return await new Promise((resolve) => {
timer = setTimeout(
() =>
resolve([
{
...DEFAULT_CURRENT,
fileVersion: 1,
},
{
...DEFAULT_CURRENT,
fileVersion: 90,
},
]),
10000,
);
});
},
);
mockMQTTEvents.message("zigbee2mqtt/bridge/request/device/ota_update/update", stringify({id: "bulb"}));
await flushPromises();
await vi.advanceTimersByTimeAsync(9000);
mockMQTTEvents.message("zigbee2mqtt/bridge/request/device/ota_update/update/abort", stringify({id: "bulb"}));
await flushPromises();
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
"zigbee2mqtt/bulb",
stringify({update: {state: "updating", progress: 0, remaining: 36001}}),
{
retain: true,
qos: 0,
},
);
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
"zigbee2mqtt/bulb",
stringify({update: {state: "updating", progress: 10, remaining: 3600}}),
{retain: true, qos: 0},
);
expect(mockMQTTPublishAsync).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({update: {state: "available"}}), {retain: true, qos: 0});
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
"zigbee2mqtt/bridge/response/device/ota_update/update/abort",
stringify({data: {id: "bulb"}, status: "ok"}),
{},
);
clearTimeout(timer);
});
it("handles abort when no running OTA", async () => {
mockMQTTEvents.message("zigbee2mqtt/bridge/request/device/ota_update/update/abort", stringify({id: "bulb"}));
await flushPromises();
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
"zigbee2mqtt/bridge/response/device/ota_update/update",
stringify({data: {}, status: "error", error: `No OTA in progress to abort for device 'bulb'`}),
{},
);
});
it("handles when OTA update fails", async () => {
devices.bulb.endpoints[0].read.mockImplementation(() => {
return {swBuildId: 1, dateCode: "2019010"};
-2
View File
@@ -283,7 +283,6 @@ export class Device {
unscheduleOta = vi.fn(() => {
this.scheduledOta = undefined;
});
abortOta = vi.fn();
scheduledOta: OtaSource | undefined = undefined;
constructor(
@@ -346,7 +345,6 @@ export class Device {
this.updateOta.mockClear();
this.scheduleOta.mockClear();
this.unscheduleOta.mockClear();
this.abortOta.mockClear();
this.meta = {};
this.scheduledOta = undefined;
-3
View File
@@ -90,7 +90,6 @@ const SETTINGS_MINIMAL_DEFAULTS = {
network_key: "GENERATE",
pan_id: "GENERATE",
ext_pan_id: "GENERATE",
enable_external_js: false,
},
frontend: {
enabled: settings.defaults.frontend!.enabled,
@@ -148,7 +147,6 @@ const SAMPLE_SETTINGS_SAVE = {
network_key: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
pan_id: 12345,
ext_pan_id: [8, 7, 6, 5, 4, 3, 2, 1],
enable_external_js: false,
},
frontend: {
enabled: true,
@@ -965,7 +963,6 @@ describe("Onboarding", () => {
network_key: "GENERATE",
pan_id: "GENERATE",
ext_pan_id: "GENERATE",
enable_external_js: false,
},
serial: {
port: SAMPLE_SETTINGS_SAVE.serial.port,