Compare commits

...
267 Commits
Author SHA1 Message Date
Koen Kanters fe048e6e66 chore(dev): release 1.38.0 (#22867) 2024-06-01 11:18:35 +02:00
Koen Kanters aa439d6687 chore: Move to googleapis/release-please-action@v4 2024-06-01 11:16:56 +02:00
Koen Kanters b5fc1689c4 chore: remove Helm chart, moved to Koenkk/zigbee2mqtt-chart 2024-06-01 11:13:21 +02:00
Koen KantersandKoenkk 33d8223e52 fix(ignore): update zigbee-herdsman-converters to 19.47.1 (#22865)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-06-01 09:05:00 +00:00
Koen KantersandKoenkk 2cc68fd862 fix(ignore): update zigbee-herdsman to 0.49.2 (#22864)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-06-01 08:49:39 +00:00
Koen KantersandKoenkk a02fd3cee6 fix(ignore): update zigbee-herdsman-converters to 19.47.0 (#22855)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-31 18:50:52 +00:00
Koen Kantersandnurikk a3ea91deb9 fix(ignore): update zigbee2mqtt-frontend to 0.7.1 (#22854)
Co-authored-by: nurikk <1525421+nurikk@users.noreply.github.com>
2024-05-31 18:45:28 +00:00
Koen KantersandKoenkk 1a12c2f15e fix(ignore): update zigbee-herdsman-converters to 19.46.0 (#22842)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-30 19:24:20 +00:00
Koen KantersandKoenkk f9b0fff185 Update zigbee-herdsman-converters to 19.45.0 (#22814)
* fix(ignore): update zigbee-herdsman-converters to 19.45.0

* fix tests

---------

Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-30 19:20:02 +00:00
Mike Ryan b6ad6418a1 feat: Add api for device re-interview (#22788)
* feat: add api for device re-interview

- Adds an API that allows for the re-interview of a device. This can be
  useful a device firmware upgrade adds new device endpoints (as is the case
  when upgrading an Inovelli VZM31-SN to 2.18). Without the ability to
  re-interview, one must remove and re-add the device.

* rename from reinterview to interview

* publish devices after interview.

* only allow device ids or names, not endpoints
2024-05-30 20:48:34 +02:00
Koen KantersandKoenkk 9f5b5d1a76 fix(ignore): update zigbee-herdsman-converters to 19.44.0 (#22801)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-27 20:29:08 +00:00
Koen KantersandKoenkk 0b3f4eb0a7 fix(ignore): update zigbee-herdsman to 0.49.1 (#22800)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-27 20:02:04 +00:00
Koen Kanters ae8a59dd51 fix(ignore): Fix Home Assistant device_automation being cleared due to ef68cc328e 2024-05-27 16:59:56 +02:00
Koen Kantersandnurikk b984a2d092 fix(ignore): update zigbee2mqtt-frontend to 0.7.0 (#22761)
Co-authored-by: nurikk <1525421+nurikk@users.noreply.github.com>
2024-05-24 08:58:34 +00:00
Koen KantersandKoenkk 9b3a39415f fix(ignore): update zigbee-herdsman to 0.49.0 (#22754)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-23 18:51:17 +00:00
Koen Kanters 3b7392ead2 fix(ignore): Revert "Update dependencies" (#22753)
This reverts commit 5ad7e1541f.
2024-05-23 20:39:30 +02:00
Koen KantersandKoenkk 5ad7e1541f fix(ignore): update dependencies (#22752)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-23 20:34:36 +02:00
Koen Kanters b2d32657f3 fix(ignore): Allow deprecated warn log_level https://github.com/Koenkk/zigbee2mqtt/issues/22706 2024-05-22 20:12:15 +02:00
Koen Kanters ef68cc328e fix: Optimize Home Assistant discovery (#22701)
* fix: Optimize Home Assistant discovery

* update

* u

* u

* u

* fix
2024-05-21 20:53:37 +02:00
Koen KantersandKoenkk 30f6c8e60c fix(ignore): update zigbee-herdsman-converters to 19.43.0 (#22730)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-21 18:22:36 +00:00
Laurent ChardinandKoen Kanters d484405cf0 fix: Expose custom clusters to MQTT (#22583)
* (feat) Expose Custom Clusters in MQTT

- Introducing bridge/definitions
- Updating test case for custom clusters
- Updating new path after ZCL revamp with 0.47.0

This change is needed for nurikk/zigbee2mqtt-frontend#2001

* Fixing test case

* Update lib/model/device.ts

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>

* Update test/bridge.test.js

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>

* Removing the publishDefinition call from some events. updating tests

---------

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2024-05-21 19:54:18 +02:00
Koen KantersandKoenkk 75608c79c8 fix(ignore): update zigbee-herdsman-converters to 19.42.0 (#22704)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-20 06:45:16 +00:00
Koen Kanters 63de2c5ccc fix: Fix Entity 'homeassistant/sensor' is unknown https://github.com/Koenkk/zigbee2mqtt/issues/22258 2024-05-19 21:32:09 +02:00
Koen KantersandKoenkk 91887c1877 fix(ignore): update zigbee-herdsman to 0.48.1 (#22699)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-19 14:12:19 +00:00
Koen Kantersandnurikk 8ab3c464dc fix(ignore): update zigbee2mqtt-frontend to 0.6.168 (#22698)
Co-authored-by: nurikk <1525421+nurikk@users.noreply.github.com>
2024-05-19 14:06:41 +00:00
Koen KantersandNerivec ce6043acb0 fix: Improve settings changing (#22674)
* Add namespaced levels for logger. Add NS to mqtt. Deprecate 'warn'.

* Improve setting validation.

* Fix setting through frontend

* Support reload + frontend improvements

* update description

* remove requiresRestart

* Fix tests.

* Fix namespaced logging at lower levels. Add better tests.

* fix: Fix settings changing

* remove `removeNullPropertiesFromObject` from bridge

* Add extra test

* update

---------

Co-authored-by: Nerivec <62446222+Nerivec@users.noreply.github.com>
2024-05-19 16:01:28 +02:00
NerivecandKoen Kanters 2eec6a4d26 fix: Add namespace-specific levels support to logger (#22619)
* Add namespaced levels for logger. Add NS to mqtt. Deprecate 'warn'.

* Improve setting validation.

* Fix setting through frontend

* Support reload + frontend improvements

* update description

* remove requiresRestart

* Fix tests.

* Fix namespaced logging at lower levels. Add better tests.

---------

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2024-05-19 15:47:40 +02:00
Koen KantersandKoenkk e9b7a847b0 fix(ignore): update zigbee-herdsman-converters to 19.41.0 (#22690)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-18 19:04:07 +00:00
Koen KantersandKoenkk e01662b3a2 fix(ignore): update zigbee-herdsman to 0.48.0 (#22689)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-18 18:54:35 +00:00
Koen KantersandKoenkk 2963397b60 fix(ignore): update zigbee-herdsman-converters to 19.40.0 (#22655)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-15 20:01:38 +00:00
Koen KantersandKoenkk 2be947b29d fix(ignore): update zigbee-herdsman-converters to 19.39.0 (#22641)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-14 20:20:10 +00:00
Koen KantersandKoenkk 4f9658132b fix(ignore): update zigbee-herdsman to 0.47.2 (#22640)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-14 22:13:00 +02:00
Koen Kanters d39baa6f1d chore: merge master to dev 2024-05-13 22:35:47 +02:00
Koen Kanters cda867a387 fix: Add Node 22 support, remove Node 21 https://github.com/Koenkk/zigbee2mqtt/issues/22595 2024-05-13 22:35:26 +02:00
Koen KantersandKoenkk 0ba02d1f40 fix(ignore): update zigbee-herdsman to 0.47.1 (#22620)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-13 20:02:50 +00:00
Koen KantersandKoenkk 09e51e064b Update zigbee-herdsman to 0.47.0 (#22602)
* fix(ignore): update zigbee-herdsman to 0.47.0

* fix imports

* u

---------

Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-12 19:34:16 +00:00
Koen Kanters 04e88baf60 fix: Fix custom cluster not available in frontend https://github.com/Koenkk/zigbee2mqtt/issues/22425 2024-05-10 22:15:38 +02:00
Koen Kanters ecf75ba4ce chore: Set mark_as_latest: false for Helm release 2024-05-10 22:08:09 +02:00
Koen Kanters 14d035007d chore: merge master to dev 2024-05-10 21:46:03 +02:00
Koen Kanters c02c61dd56 chore: Trigger Helm chart release 2024-05-10 21:45:41 +02:00
Koen Kanters 6b60c4ea41 chore: merge master to dev 2024-05-09 22:44:46 +02:00
Koen Kanters d043fd56f9 chore: update problem_report.yaml 2024-05-09 22:44:27 +02:00
Koen Kanters 297e904c2f chore: merge master to dev 2024-05-09 22:38:56 +02:00
Koen Kanters 34f0332564 chore: fix helm_release.yaml branch 2024-05-09 22:38:39 +02:00
github-actions[bot] 9e83b7bba2 chore: promote to dev 2024-05-09 20:33:49 +00:00
Koen Kantersandgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> ea39d86e6b chore: release 1.37.1 (#22569)
* chore: release dev

* chore(release): update helm-chart docs

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-09 22:23:15 +02:00
Koen KantersandKoenkk 06fd8a4eb1 fix(ignore): update zigbee-herdsman-converters to 19.37.2 (#22570)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-09 20:14:10 +00:00
Nerivec d26ccaed65 fix: Fix logger crashing on restart (#22565) 2024-05-09 21:42:28 +02:00
Koen KantersandKoenkk 989f667c81 fix(ignore): update zigbee-herdsman to 0.46.6 (#22558)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-09 06:17:53 +00:00
Koen Kanters cce41c20fb chore: set release-please pull-request-title-pattern 2024-05-08 23:43:55 +02:00
Koen Kanters 350c682e8f chore: make tests faster 2024-05-08 23:32:46 +02:00
Koen KantersandKoenkk 6bcb4b66b1 fix(ignore): update zigbee-herdsman to 0.46.5 (#22552)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-08 21:16:13 +00:00
Koen Kanters 58d37b0f92 chore: improve test speed 2024-05-08 23:09:23 +02:00
Koen Kanters aecdfe638b chore: another attempt to fix release_please 2024-05-08 22:55:46 +02:00
Koen KantersandKoenkk cc9a95c74e fix(ignore): update zigbee-herdsman-converters to 19.37.1 (#22549)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-08 20:49:51 +00:00
Koen Kanters 834626d2e9 chore: fix release_please.yml 2024-05-08 22:44:30 +02:00
Koen KantersandKoenkk 6b886f6268 fix(ignore): update zigbee-herdsman-converters to 19.37.0 (#22531)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-07 20:49:28 +00:00
Koen Kanters a0c9a1769f fix: Fix MQTT ECONNREFUSED error https://github.com/zigbee2mqtt/hassio-zigbee2mqtt/issues/605 2024-05-07 21:25:01 +02:00
Koen KantersandKoenkk 3544d5172e fix(ignore): update zigbee-herdsman-converters to 19.36.1 (#22503)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-06 20:00:06 +00:00
Koen KantersandKoenkk e44608a46d fix(ignore): update zigbee-herdsman to 0.46.4 (#22501)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-06 19:55:04 +00:00
Jose Luis Pedrosaandjose 6a69c16170 chore: Release please no longer overwrites .release-please-manifest.json (#22499)
Co-authored-by: jose <jose.pedrosa@reddit.com>
2024-05-06 21:43:55 +02:00
Jose Luis Pedrosaandjose b2e8b3e00c fix: Add Helm chart (#21852)
* Initial draft for helm chart

* Add CI using helm releaser

Add helm chart docummentation

Add configuration.yaml values

* Set maintainer

set right defaults on values.yaml

* Add ci to update values on chart

* Add chart version on CI

* Remove manual update of the chart

* Trigger update of Chart.yaml with release_please

* Add chart to release-please

* Add version to release-please manifest for chart

* remove the second release-please

* Update appVersion in chart.yaml too

* Add make target to generate helm-docs

* Generate docs and manage versions in release-please

* Add persistent static volumes

* Small fixes, ensure no volume works

* Fixes for static provisioned volume

* Delete yq dependency in CI

* Delete Makefile, update release-please to run doc generation directly

---------

Co-authored-by: jose <jose.pedrosa@reddit.com>
2024-05-06 20:30:09 +02:00
Koen KantersandKoenkk 358e2eee45 fix(ignore): update zigbee-herdsman-converters to 19.36.0 (#22472)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-05 20:45:29 +00:00
Koen KantersandKoenkk 316654b5d3 fix(ignore): update zigbee-herdsman to 0.46.3 (#22471)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-05 20:32:55 +00:00
Nerivec eb49f523bf fix: Add settings for finer control over debug log level (#22426)
* Add settings for finer control over debug log level.

* Updates from feedback.
2024-05-05 21:52:26 +02:00
Koen KantersandKoenkk b754b9d463 fix(ignore): update dependencies (#22456)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-05 21:44:20 +02:00
Koen KantersandKoenkk e4c9bac348 fix(ignore): update zigbee-herdsman to 0.46.2 (#22470)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-05 19:05:33 +00:00
Koen KantersandKoenkk beedfaefac fix(ignore): update zigbee-herdsman-converters to 19.35.0 (#22442)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-04 08:34:32 +00:00
Koen KantersandKoenkk 329d832337 fix(ignore): update zigbee-herdsman to 0.46.1 (#22439)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-04 07:00:44 +00:00
Koen Kantersandnurikk 23e97d0001 fix(ignore): update zigbee2mqtt-frontend to 0.6.167 (#22419)
Co-authored-by: nurikk <1525421+nurikk@users.noreply.github.com>
2024-05-03 08:44:34 +00:00
Koen KantersandKoenkk 3a61f0acfa Update zigbee-herdsman-converters to 19.34.0 (#22411)
* fix(ignore): update zigbee-herdsman-converters to 19.34.0

* fix tests

---------

Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-02 20:20:06 +00:00
Koen Kantersandnurikk 5899436d74 fix(ignore): update zigbee2mqtt-frontend to 0.6.166 (#22410)
Co-authored-by: nurikk <1525421+nurikk@users.noreply.github.com>
2024-05-02 20:00:06 +00:00
Koen Kanters 97d5c5c97a chore: merge master to dev 2024-05-02 21:18:48 +02:00
rbswift ee5cf5a901 chore: Improve update.sh (#22395)
Pulling without specifying how to reconcile divergent branches is discouraged and produces an ugly "hint" that can be a little disconcerting when running update.sh
2024-05-02 21:18:25 +02:00
Koen KantersandKoenkk 907591c467 fix(ignore): update zigbee-herdsman-converters to 19.33.0 (#22381)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-01 20:28:30 +00:00
Koen KantersandKoenkk 55b5f91a96 fix(ignore): update zigbee-herdsman to 0.46.0 (#22380)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-01 20:20:29 +00:00
github-actions[bot] ce915a6797 chore: promote to dev 2024-05-01 19:45:20 +00:00
Koen Kanters 46f34c8cc7 chore(dev): release 1.37.0 (#22022) 2024-05-01 21:23:10 +02:00
Koen KantersandKoenkk e03013c581 fix(ignore): update zigbee-herdsman-converters to 19.32.0 (#22377)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-05-01 19:17:13 +00:00
Koen Kantersandnurikk 8a288a9119 fix(ignore): update zigbee2mqtt-frontend to 0.6.165 (#22366)
Co-authored-by: nurikk <1525421+nurikk@users.noreply.github.com>
2024-04-30 19:47:10 +00:00
Koen KantersandKoenkk 8e27ec24f6 fix(ignore): update zigbee-herdsman to 0.45.0 (#22365)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-30 19:38:26 +00:00
Koen KantersandKoenkk 4b291bf012 Update zigbee-herdsman-converters to 19.31.1 (#22341)
* fix(ignore): update zigbee-herdsman-converters to 19.31.1

* fix tests

---------

Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-29 20:01:56 +00:00
Koen Kantersandnurikk 10e6ad0e67 fix(ignore): update zigbee2mqtt-frontend to 0.6.164 (#22340)
Co-authored-by: nurikk <1525421+nurikk@users.noreply.github.com>
2024-04-28 18:23:36 +00:00
Koen KantersandKoenkk 1b089ef6ae fix(ignore): update zigbee-herdsman to 0.44.0 (#22339)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-28 18:11:30 +00:00
Koen KantersandKoenkk d7e2d98a5a fix(ignore): update dependencies (#22331)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-28 08:56:01 +02:00
Koen Kanters eb69319674 chore: merge master to dev 2024-04-28 08:53:39 +02:00
Koen Kanters 8c8a6a3ecf chore: don't autoupdate eslint 2024-04-28 08:53:17 +02:00
Koen Kanters baa1d703d4 chore: don't autoupdate eslint 2024-04-28 08:50:56 +02:00
Koen KantersandKoenkk 37551265a2 fix(ignore): update zigbee-herdsman-converters to 19.29.0 (#22325)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-27 07:19:33 +00:00
Koen KantersandKoenkk 245afb1126 fix(ignore): update zigbee-herdsman-converters to 19.28.0 (#22318)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-25 20:31:11 +00:00
Koen KantersandKoenkk 389779d4e3 fix(ignore): update zigbee-herdsman to 0.43.1 (#22317)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-25 19:50:10 +00:00
Koen KantersandKoenkk df23240683 fix(ignore): update zigbee-herdsman-converters to 19.27.0 (#22301)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-23 20:46:01 +00:00
Koen KantersandKoenkk 75f9743528 fix(ignore): update zigbee-herdsman to 0.43.0 (#22300)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-23 20:25:41 +00:00
Koen Kantersandnurikk e5d632bc91 fix(ignore): update zigbee2mqtt-frontend to 0.6.163 (#22288)
Co-authored-by: nurikk <1525421+nurikk@users.noreply.github.com>
2024-04-22 11:31:56 +00:00
Koen KantersandKoenkk 4f97a7ad2c fix(ignore): update zigbee-herdsman to 0.42.5 (#22285)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-21 20:47:08 +00:00
Koen KantersandKoenkk 4bf2ffa7dd fix(ignore): update zigbee-herdsman-converters to 19.26.0 (#22284)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-21 19:09:52 +00:00
Koen KantersandKoenkk a8f6bec673 fix(ignore): update zigbee-herdsman to 0.42.4 (#22283)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-21 19:01:53 +00:00
Koen KantersandKoenkk 80504b943d fix(ignore): update zigbee-herdsman-converters to 19.25.0 (#22266)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-20 10:04:27 +00:00
Koen KantersandKoenkk d9628df17b fix(ignore): update zigbee-herdsman-converters to 19.24.0 (#22254)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-18 20:29:12 +00:00
Koen KantersandKoenkk f7abbd5f5a fix(ignore): update zigbee-herdsman to 0.42.3 (#22253)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-18 20:15:59 +00:00
Koen KantersandKoenkk 1544317fce fix(ignore): update zigbee-herdsman-converters to 19.23.0 (#22232)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-16 19:49:38 +00:00
Koen KantersandKoenkk 172aa7461a fix(ignore): update zigbee-herdsman-converters to 19.22.0 (#22221)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-15 20:13:35 +00:00
Koen KantersandKoenkk 76f5425452 fix(ignore): update zigbee-herdsman to 0.42.2 (#22220)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-15 19:55:40 +00:00
Koen KantersandKoenkk 87aa18d14c fix(ignore): update zigbee-herdsman-converters to 19.21.0 (#22199)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-13 08:07:00 +00:00
Koen KantersandKoenkk e10ab5ccb2 fix(ignore): update zigbee-herdsman-converters to 19.20.0 (#22187)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-11 19:46:11 +00:00
f774753213 fix(ignore): update zigbee-herdsman-converters to 19.19.1 (#22171)
* fix(ignore): update zigbee-herdsman-converters to 19.19.1

* fix(configure): pass definition to function call (#22154)

* Pass definition to configure

* fix test

---------

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>

---------

Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
Co-authored-by: Sergei Salimov <32013287+mrskycriper@users.noreply.github.com>
2024-04-10 22:02:12 +02:00
Koen KantersandKoenkk a210c238f1 fix(ignore): update zigbee-herdsman to 0.42.1 (#22143)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-10 19:56:41 +00:00
Koen KantersandKoenkk cdbbe0d49f fix(ignore): update zigbee-herdsman-converters to 19.19.0 (#22153)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-09 19:21:28 +00:00
Koen Kanters c98e5a74de fix: Remove deprecated Home Assistant color_mode (#22142) 2024-04-08 21:19:17 +02:00
Koen KantersandKoenkk 8b8d660b06 fix(ignore): update zigbee-herdsman-converters to 19.18.0 (#22141)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-08 19:14:41 +00:00
Nerivec 3c1a4afcb7 fix: Convert logger into a class (#22129) 2024-04-07 20:52:55 +02:00
Koen KantersandKoenkk cabc9f5e2f fix(ignore): update zigbee-herdsman-converters to 19.17.0 (#22122)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-07 08:46:33 +00:00
Koen KantersandKoenkk 28b8aff1ae fix(ignore): update zigbee-herdsman to 0.42.0 (#22121)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-07 10:42:45 +02:00
Koen KantersandKoenkk e7487189cf fix(ignore): update dependencies (#22007)
* fix(ignore): update dependencies

* fix

---------

Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-06 13:02:02 +02:00
Koen Kanters 92f53235f3 fix: Fix crash when stopping in case MQTT client did not connect yet (#22110) 2024-04-06 10:49:22 +00:00
Koen Kanters 002050f35b fix: Reduce MQTT publishing by excluding debug logging from bridge/logging (#22066)
* fix: Reduce MQTT publishing by excluding debug logging from `bridge/logging`

* fix tests

* fix
2024-04-06 12:44:43 +02:00
Koen KantersandKoenkk 394a3fbd8a fix(ignore): update zigbee-herdsman-converters to 19.16.0 (#22107)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-06 09:46:26 +00:00
Koen Kanters af3a0332f9 chore: Update stale.yml 2024-04-06 08:33:30 +02:00
Koen Kantersandnurikk 0c80129466 fix(ignore): update zigbee2mqtt-frontend to 0.6.162 (#22094)
Co-authored-by: nurikk <1525421+nurikk@users.noreply.github.com>
2024-04-05 11:51:31 +00:00
Koen KantersandKoenkk ae35aaed5e fix(ignore): update zigbee-herdsman-converters to 19.15.0 (#22089)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-04 20:06:53 +00:00
Koen Kanters 9d251dbad5 fix: Disable automatic reconfigure when configureKey changes (#22088) 2024-04-04 21:47:44 +02:00
Koen KantersandKoenkk a020138f6b fix(ignore): update zigbee-herdsman to 0.41.2 (#22087)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-04 19:36:33 +00:00
Koen KantersandKoenkk 701cdda38c Update zigbee-herdsman-converters to 19.14.0 (#22065)
* fix(ignore): update zigbee-herdsman-converters to 19.14.0

* fix tests

---------

Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-04 19:33:39 +00:00
Nerivec 919c4eee73 fix: Cleanup old logger leftovers (#22069) 2024-04-04 20:24:39 +02:00
Koen KantersandKoenkk 3e913495b1 fix(ignore): update zigbee-herdsman to 0.41.1 (#22064)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-03 18:26:22 +00:00
Koen Kanters bbb6f9e042 fix: Change MQTT publish log to debug (#22063) 2024-04-03 18:13:46 +00:00
Koen Kanters cdd6fb0864 fix(ignore): fix 73566df 2024-04-02 22:39:33 +02:00
Koen Kanters 780c065fa1 fix(ignore): fix 73566dff98 2024-04-02 22:38:02 +02:00
Koen KantersandKoenkk db7d9e1120 fix(ignore): update zigbee-herdsman-converters to 19.13.0 (#22046)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-02 22:37:10 +02:00
NerivecandKoen Kanters 73566dff98 feat: Unified logging across z2m/zh/zhc (#21984)
* Unified logging across z2m/zh/zhc

* Updates

---------

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2024-04-02 22:33:51 +02:00
Koen KantersandKoenkk bd0a41107a fix(ignore): update zigbee-herdsman to 0.41.0 (#22045)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-02 22:33:26 +02:00
Koen KantersandKoenkk 62844ff637 Update zigbee-herdsman-converters to 19.12.1 (#22044)
* fix(ignore): update zigbee-herdsman-converters to 19.12.1

* fix tests

---------

Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-02 20:17:43 +00:00
github-actions[bot] 5077b03a02 chore: promote to dev 2024-04-01 18:06:23 +00:00
Koen Kanters ffc2ff1d0e chore(dev): release 1.36.1 (#21638) 2024-04-01 19:52:17 +02:00
Koen KantersandKoenkk 3aadab0925 fix(ignore): update zigbee-herdsman-converters to 19.11.2 (#22021)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-04-01 17:49:24 +00:00
Koen KantersandKoenkk 85c00b8b43 fix(ignore): update zigbee-herdsman-converters to 19.11.1 (#22014)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-31 19:12:44 +00:00
Koen KantersandKoenkk e51792c65a fix(ignore): update zigbee-herdsman-converters to 19.11.0 (#22013)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-31 18:44:03 +00:00
Koen KantersandKoenkk 1e74b2f53f fix(ignore): update zigbee-herdsman to 0.40.3 (#22010)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-31 08:01:06 +00:00
Koen KantersandKoenkk 886b5345d7 fix(ignore): update zigbee-herdsman to 0.40.2 (#22000)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-30 10:14:12 +00:00
Koen KantersandKoenkk 7d8b5374ad fix(ignore): update zigbee-herdsman-converters to 19.10.0 (#21996)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-29 19:48:10 +00:00
Koen KantersandKoenkk 0c7730f4c5 fix(ignore): update zigbee-herdsman to 0.40.1 (#21987)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-28 20:08:42 +00:00
Koen KantersandKoenkk a7aab714ba fix(ignore): update zigbee-herdsman-converters to 19.9.0 (#21970)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-27 19:57:56 +00:00
Koen KantersandKoenkk 032b9a096e fix(ignore): update zigbee-herdsman-converters to 19.8.0 (#21957)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-26 19:47:40 +00:00
Koen KantersandKoenkk 1f3d61dc7f Update zigbee-herdsman-converters to 19.7.0 (#21947)
* fix(ignore): update zigbee-herdsman-converters to 19.7.0

* fix tests

---------

Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-25 21:24:00 +00:00
Koen KantersandKoenkk 32b173c653 fix(ignore): update zigbee-herdsman-converters to 19.6.0 (#21927)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-24 13:48:20 +00:00
Koen KantersandKoenkk 8c4cd8c9aa fix(ignore): update dependencies (#21923)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-24 08:06:46 +01:00
Koen KantersandKoenkk 73423ebd60 fix(ignore): update zigbee-herdsman-converters to 19.5.0 (#21902)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-21 19:35:31 +00:00
Koen KantersandKoenkk 334e260b85 fix(ignore): update zigbee-herdsman to 0.40.0 (#21901)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-21 19:32:32 +00:00
Koen Kanters 731957dab9 chore: merge master to dev 2024-03-21 20:10:51 +01:00
wexmanandJens Weiermann 9c505fd75f chore: optimized svg logo (#21874)
* optimized svg image

* preserved original border

---------

Co-authored-by: Jens Weiermann <j.weiermann@wilms.com>
2024-03-21 20:10:33 +01:00
Koen KantersandKoenkk 25cec20f0d fix(ignore): update zigbee-herdsman-converters to 19.4.2 (#21892)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-20 20:40:45 +00:00
Koen KantersandKoenkk f05d501380 fix(ignore): update zigbee-herdsman to 0.39.1 (#21891)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-20 20:27:30 +00:00
Koen KantersandKoenkk b02c34c6e5 fix(ignore): update zigbee-herdsman-converters to 19.4.1 (#21872)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-19 20:57:38 +00:00
Koen KantersandKoenkk 801134b630 fix(ignore): update zigbee-herdsman to 0.39.0 (#21871)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-19 20:50:21 +00:00
Koen KantersandKoenkk 73d43007d4 fix(ignore): update zigbee-herdsman-converters to 19.4.0 (#21870)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-19 20:30:52 +00:00
Koen KantersandKoenkk 1a9e05e6ab fix(ignore): update zigbee-herdsman-converters to 19.3.0 (#21851)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-17 20:21:30 +00:00
Koen KantersandKoenkk 1006725b09 fix(ignore): update dependencies (#21832)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-17 10:18:56 +01:00
Koen KantersandKoenkk a4f9c49ab5 fix(ignore): update zigbee-herdsman-converters to 19.2.1 (#21801)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-14 19:58:50 +00:00
Koen KantersandKoenkk 658622880f fix(ignore): update zigbee-herdsman to 0.38.1 (#21802)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-14 20:55:17 +01:00
Koen KantersandKoenkk 720fc491ce fix(ignore): update zigbee-herdsman-converters to 19.1.1 (#21789)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-13 19:45:48 +00:00
Koen KantersandKoenkk 56feb77f2d fix(ignore): update zigbee-herdsman-converters to 19.1.0 (#21778)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-12 20:42:14 +00:00
Koen KantersandKoenkk 233d263e13 fix(ignore): update zigbee-herdsman to 0.38.0 (#21777)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-12 20:32:24 +00:00
Koen KantersandKoenkk dbd616fbe5 fix(ignore): update zigbee-herdsman-converters to 19.0.0 (#21768)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-11 20:51:22 +00:00
Koen KantersandKoenkk 86c2e90e06 Update zigbee-herdsman-converters to 18.49.0 (#21767)
* fix(ignore): update zigbee-herdsman-converters to 18.49.0

* fix tests

---------

Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-11 20:40:55 +00:00
Koen KantersandKoenkk 43610bbff4 fix(ignore): Update dependencies (#21740)
* fix(ignore): update dependencies

* fix

* fix

---------

Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-10 19:08:48 +00:00
Koen Kanters 29543bad4b fix(ignore): fix tests 2024-03-10 19:54:35 +01:00
Koen KantersandKoenkk 2a047a469c fix(ignore): update zigbee-herdsman-converters to 18.48.0 (#21753)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-10 19:45:41 +01:00
Koen Kanters 83502f2d30 fix(ignore): Update manufacturer codes (#21729) 2024-03-10 19:45:22 +01:00
Koen KantersandKoenkk b60f679814 fix(ignore): update zigbee-herdsman to 0.37.0 (#21743)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-10 19:41:50 +01:00
Sergei Salimov 14e044093c fix: Home Assistant: remove button name if device_class is present (#21726)
* Remove preset name if button has device class

* fix device_class for consumer_connected

* combine device_class checks
2024-03-08 22:39:15 +01:00
Koen KantersandKoenkk 3188038a7b fix(ignore): update zigbee-herdsman-converters to 18.47.0 (#21728)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-08 21:36:44 +00:00
Koen KantersandKoenkk ebf996acd8 fix(ignore): update zigbee-herdsman to 0.36.2 (#21727)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-08 21:26:15 +00:00
Koen KantersandKoenkk 0e0eccbd13 fix(ignore): update zigbee-herdsman-converters to 18.46.0 (#21714)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-07 21:03:26 +00:00
Koen KantersandKoenkk e834e93deb fix(ignore): update zigbee-herdsman to 0.36.1 (#21713)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-07 20:05:21 +00:00
Koen KantersandKoenkk d37c164993 fix(ignore): update zigbee-herdsman-converters to 18.45.0 (#21699)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-06 19:36:32 +00:00
Koen KantersandKoenkk 6daa7aa61d fix(ignore): update zigbee-herdsman-converters to 18.44.0 (#21690)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-05 21:36:10 +00:00
koenkk 1110d3f8ef fix: Fix incorrect update state when Z2M is restarted during OTA update https://github.com/Koenkk/zigbee2mqtt/issues/12794 2024-03-05 22:33:33 +01:00
Koen KantersandKoenkk 1d73d9d476 fix(ignore): update zigbee-herdsman to 0.36.0 (#21689)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-05 20:51:14 +00:00
Simon RettbergandKoen Kanters 2da80954d6 fix: Fix crash due to race condition during interview (#21680)
Fixes #21118
Mentioned in #21036 and #20688

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2024-03-05 21:28:35 +01:00
koenkk e2fa66a38c chore: fix ci 2024-03-05 21:23:07 +01:00
koenkk 94382ee418 chore: fix ci 2024-03-05 21:19:56 +01:00
Koen KantersandKoenkk 94c92ef021 Update zigbee-herdsman-converters to 18.43.0 (#21678)
* fix(ignore): update zigbee-herdsman-converters to 18.43.0

* Update configure.test.js

---------

Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-04 21:44:31 +00:00
Koen KantersandKoenkk 8c86849bb9 fix(ignore): update dependencies (#21657)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-04 22:32:11 +01:00
Koen KantersandKoenkk 30909d57fb fix(ignore): update zigbee-herdsman to 0.35.3 (#21677)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-04 21:23:13 +00:00
Koen Kantersandnurikk aaed8ab95e fix(ignore): update zigbee2mqtt-frontend to 0.6.161 (#21660)
Co-authored-by: nurikk <1525421+nurikk@users.noreply.github.com>
2024-03-03 09:39:49 +00:00
Koen Kanters f24b20b133 chore: merge master to dev 2024-03-02 22:19:13 +01:00
koenkk 2a53b8e59b chore: fix release ci 2024-03-02 22:18:48 +01:00
Koen KantersandKoenkk ccaa1281a3 fix(ignore): update zigbee-herdsman to 0.35.2 (#21654)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-02 21:16:37 +00:00
Sergei Salimov 01843fe6c4 fix: Add more Home Assistant entity icons (#21652) 2024-03-02 21:46:46 +01:00
Koen Kanters 66fb672492 fix: Fix set endpoint name conversion (#21653)
* fix: Fix `set` endpoint name conversion https://github.com/Koenkk/zigbee2mqtt/issues/21646

* fix
2024-03-02 21:45:19 +01:00
koenkk 38ed953599 chore: promote to dev 2024-03-01 21:17:51 +01:00
Koen Kanters 86ed71cb1f chore(dev): release 1.36.0 (#21266) 2024-03-01 20:58:11 +01:00
Koen KantersandKoenkk ccc31670ec fix(ignore): update zigbee-herdsman-converters to 18.42.0 (#21637)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-01 19:50:46 +00:00
Koen KantersandKoenkk 0d8f5780dd fix(ignore): update zigbee-herdsman to 0.35.1 (#21636)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-03-01 19:45:01 +00:00
Koen Kantersandnurikk a8a262640f fix(ignore): update zigbee2mqtt-frontend to 0.6.159 (#21623)
Co-authored-by: nurikk <1525421+nurikk@users.noreply.github.com>
2024-02-29 20:45:12 +00:00
Koen KantersandKoenkk 72333f0888 fix(ignore): update zigbee-herdsman-converters to 18.41.0 (#21622)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-29 20:20:07 +00:00
Koen KantersandKoenkk a84083be40 fix(ignore): update zigbee-herdsman to 0.35.0 (#21621)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-29 16:57:19 +00:00
Sergei Salimov 9b8583e778 fix: Add noise and noise_detected to HA lookup (#21610) 2024-02-29 17:42:48 +01:00
Koen KantersandKoenkk aeaa040920 fix(ignore): update zigbee-herdsman-converters to 18.40.0 (#21603)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-27 20:04:02 +00:00
Koen KantersandKoenkk 222b67b1b0 fix(ignore): update zigbee-herdsman to 0.34.9 (#21602)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-27 19:04:44 +00:00
Koen KantersandKoenkk 1ee1710a96 Update zigbee-herdsman-converters to 18.39.1 (#21590)
* fix(ignore): update zigbee-herdsman-converters to 18.39.1

* fix

---------

Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-26 21:32:08 +00:00
Koen Kanters a1e2ee7837 chore: merge master to dev 2024-02-26 21:45:09 +01:00
koenkk f04ade6d76 chore: Update problem_report template 2024-02-26 21:44:00 +01:00
Koen KantersandKoenkk fee0811893 fix(ignore): update zigbee-herdsman to 0.34.8 (#21588)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-26 19:21:45 +00:00
Koen Kanters 956003aa0a fix: Fix endpoint matching for TuYa dummy endpoints (#21576) 2024-02-26 20:02:05 +01:00
Koen KantersandKoenkk 8fb28f9e2f Update zigbee-herdsman-converters to 18.38.0 (#21568)
* fix(ignore): update zigbee-herdsman-converters to 18.38.0

* fix tests

---------

Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-25 21:09:35 +00:00
Koen KantersandKoenkk a06a0ea059 fix(ignore): update zigbee-herdsman to 0.34.7 (#21575)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-25 21:04:49 +00:00
Koen KantersandKoenkk 4f71c1e8bf fix(ignore): update zigbee-herdsman to 0.34.6 (#21567)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-25 13:57:37 +00:00
Koen KantersandKoenkk c56470c83f fix(ignore): update dependencies (#21561)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-25 14:12:46 +01:00
Koen KantersandKoenkk cc27032752 fix(ignore): update zigbee-herdsman to 0.34.5 (#21563)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-25 09:03:54 +00:00
Koen KantersandKoenkk 7c97902d4a fix(ignore): update zigbee-herdsman-converters to 18.37.1 (#21549)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-24 08:31:39 +00:00
Koen KantersandKoenkk 23959c5b0e fix(ignore): update zigbee-herdsman to 0.34.4 (#21545)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-23 20:40:51 +00:00
Koen KantersandKoenkk e726d5907e fix(ignore): update zigbee-herdsman to 0.34.3 (#21530)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-22 19:37:33 +00:00
Koen KantersandKoenkk 1337f54e4a fix(ignore): update zigbee-herdsman-converters to 18.37.0 (#21533)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-22 19:32:59 +00:00
Oleksandr Masliuchenko 471902e531 fix: Improve nested group and device name match (#21521) 2024-02-22 19:54:09 +01:00
Koen KantersandKoenkk 22d33360f6 fix(ignore): update zigbee-herdsman-converters to 18.36.0 (#21504)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-20 20:04:54 +00:00
koenkk db1e3b73d1 fix: Only configure routers on startup https://github.com/Koenkk/zigbee2mqtt/issues/21446 2024-02-20 20:24:03 +01:00
Artem Draft fe73db4ff6 fix: trigger_count & power_outage_count adjustments in HA (#21492) 2024-02-20 19:47:45 +01:00
Koen Kanters 9dd6eeee7b chore: merge master to dev 2024-02-20 19:47:25 +01:00
Oleksandr Masliuchenko 3c609658fd fix: Fix parsing entity name that contains slash in the name and endpoint specification (#21493) 2024-02-20 19:47:22 +01:00
Anthony Bourguignon 8b694432aa fix: Check if Zigbee2MQTT is running in update.sh (#21494) 2024-02-20 19:46:44 +01:00
Koen KantersandKoenkk b32bd309ca fix(ignore): update zigbee-herdsman-converters to 18.35.0 (#21487)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-19 21:08:45 +00:00
Koen KantersandKoenkk 78b5e6fd4b fix(ignore): update zigbee-herdsman to 0.34.2 (#21478)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-19 20:46:48 +00:00
Koen Kanters c5c0a8b193 feat: Add experimental Ember adapter @Nerivec https://github.com/Koenkk/zigbee-herdsman/pull/918 (#21459) 2024-02-18 22:11:09 +01:00
Koen KantersandKoenkk b51eef2319 fix(ignore): update zigbee-herdsman to 0.34.1 (#21461)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-18 21:01:03 +00:00
Koen KantersandKoenkk ebed9d31d7 fix(ignore): update zigbee-herdsman-converters to 18.34.0 (#21456)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-18 16:39:07 +00:00
Koen KantersandKoenkk 16b251d3c9 fix(ignore): update dependencies (#21444)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-18 16:23:36 +01:00
Koen KantersandKoenkk 5766bf479b fix(ignore): update zigbee-herdsman-converters to 18.33.2 (#21416)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-15 19:01:26 +00:00
Koen KantersandKoenkk 8a9477f2bb fix(ignore): update zigbee-herdsman-converters to 18.33.1 (#21400)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-14 18:49:57 +00:00
Koen KantersandKoenkk 2aaf878889 fix(ignore): update zigbee-herdsman-converters to 18.33.0 (#21387)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-13 19:28:16 +00:00
Koen KantersandKoenkk dfa499659e fix(ignore): update zigbee-herdsman to 0.33.9 (#21386)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-13 19:05:11 +00:00
Sergei SalimovandKoen Kanters 24b5f9b43a feat: Add support for button entity in Home Assistant (#21372)
* add button entity support

* add enabled_by_default to select

---------

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2024-02-13 19:43:21 +01:00
Koen KantersandKoenkk 0ab7390a64 fix(ignore): update zigbee-herdsman-converters to 18.32.0 (#21373)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-12 20:31:38 +00:00
Koen KantersandKoenkk c7b820c376 fix(ignore): update zigbee-herdsman-converters to 18.31.0 (#21359)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-11 20:46:36 +00:00
Koen KantersandKoenkk ce41f2f8ba fix(ignore): update dependencies (#21347)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-11 21:07:38 +01:00
Koen Kantersandnurikk 8bc3c46c34 fix(ignore): update zigbee2mqtt-frontend to 0.6.158 (#21356)
Co-authored-by: nurikk <1525421+nurikk@users.noreply.github.com>
2024-02-11 18:17:21 +00:00
Oleksandr Masliuchenko b4473d6e24 fix: Remove predefined endpoint list, remove limits on device friendly name (#21325)
* Get rid of predefined list of endpoints, correct friend name validations

* Fix minor bug while parsing a topic for an unknown device
2024-02-10 16:50:44 +01:00
Koen KantersandKoenkk 910f3a6860 fix(ignore): update zigbee-herdsman-converters to 18.30.0 (#21315)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-08 19:58:46 +00:00
Oleksandr MasliuchenkoandKoen Kanters 81335e5b4b fix: Remove dependency on predefined list of endpoints (parseEntityID() function) (#21264)
* Add function resolveEntityByID() function as an improved alternative for parseEntityID()

* Port deviceGroupMembership.ts from parseEntityID() to resolveEntityByID()

* Migrade groups.ts from parseEntityID() to resolveEntityByID()

* Improve test coverage on groups functionality

* Migrate from parseEntityID() to resolveEntityByID() for bridge functionality

* Migrate from parseEntityID() to resolveEntityByID() for bind functionality

* Finally get rid of parseEntityID() function

* Fix linter issues

* Move resolveEntityAndEndpoint() function to zigbee.ts

---------

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2024-02-08 20:54:05 +01:00
Koen KantersandKoenkk 55653c974e fix(ignore): update zigbee-herdsman-converters to 18.29.1 (#21290)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-07 20:32:47 +00:00
Oleksandr MasliuchenkoandKoen Kanters a5a87a79a8 fix: Match endpoint name in the MQTT topic based on endpoints listed in the definition (#21214)
* Add endpoint matching based on the endpoint names list in the device definition

* Cleanup debug prints

* Correct creating endpoint names list, filter out null elements

* Correct test due to slightly changed behavior

* Fix endpoint name lookup when converter does not provide full name-to-id mapping

* Simplify topic name matching

* Remove obsolete branches, improve code coverage

* Make linter happy

* Make linter happy

* Remove dependency on endpoint names while removing device from all groups

* Get rid of predefined list of endpoints when processing group state update

---------

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2024-02-07 20:12:48 +01:00
Koen Kantersandnurikk a479bf9039 fix(ignore): update zigbee2mqtt-frontend to 0.6.157 (#21269)
Co-authored-by: nurikk <1525421+nurikk@users.noreply.github.com>
2024-02-06 21:32:15 +00:00
koenkk b7e19cc86a chore: promote to dev 2024-02-06 21:06:28 +01:00
Koen Kanters fe0742a628 chore(dev): release 1.35.3 (#21139) 2024-02-06 20:48:14 +01:00
Koen KantersandKoenkk 29bae3a734 fix(ignore): update zigbee-herdsman-converters to 18.28.3 (#21265)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-06 19:44:44 +00:00
Koen Kanters 6aa6f04059 chore: merge master to dev 2024-02-06 17:46:32 +01:00
Issy Szemeti fc6d74e2b7 chore: Fix spelling/grammar (#21260) 2024-02-06 17:46:13 +01:00
Koen KantersandKoenkk 92e0d7895b Update zigbee-herdsman to 0.33.8 (#21246)
* fix(ignore): update zigbee-herdsman to 0.33.8

* fix

---------

Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-05 19:57:03 +00:00
Koen KantersandKoenkk 250d5bfc24 fix(ignore): update zigbee-herdsman-converters to 18.28.2 (#21245)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-05 19:38:54 +00:00
Koen Kantersandnurikk dfda63138f fix(ignore): update zigbee2mqtt-frontend to 0.6.156 (#21229)
Co-authored-by: nurikk <1525421+nurikk@users.noreply.github.com>
2024-02-04 22:35:57 +00:00
Koen Kantersandnurikk 8ab6c16f6e fix(ignore): update zigbee2mqtt-frontend to 0.6.155 (#21228)
Co-authored-by: nurikk <1525421+nurikk@users.noreply.github.com>
2024-02-04 22:20:13 +00:00
Koen KantersandKoenkk a6d51a60a8 fix(ignore): update zigbee-herdsman-converters to 18.28.1 (#21222)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-04 20:05:53 +00:00
Koen KantersandKoenkk 3d37892296 fix(ignore): update zigbee-herdsman-converters to 18.28.0 (#21207)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-04 08:28:03 +00:00
Koen KantersandKoenkk 1df54f071f fix(ignore): update dependencies (#21204)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-04 09:16:48 +01:00
koenkk b3253bacc4 chore: remove @types/rimraf 2024-02-04 09:13:03 +01:00
Koen KantersandKoenkk 4b1721721b fix(ignore): update zigbee-herdsman-converters to 18.27.1 (#21189)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-03 13:57:30 +00:00
Koen Kantersandnurikk 2db07236db fix(ignore): update zigbee2mqtt-frontend to 0.6.154 (#21188)
Co-authored-by: nurikk <1525421+nurikk@users.noreply.github.com>
2024-02-03 12:14:17 +00:00
Koen Kantersandnurikk 7295955397 fix(ignore): update zigbee2mqtt-frontend to 0.6.153 (#21187)
Co-authored-by: nurikk <1525421+nurikk@users.noreply.github.com>
2024-02-03 12:10:41 +00:00
koenkk f83709d96c fix(ignore): set zhc logger 2024-02-03 12:27:04 +01:00
Koen KantersandKoenkk a678a1c652 fix(ignore): update zigbee-herdsman-converters to 18.27.0 (#21184)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-03 11:24:11 +00:00
Koen KantersandKoenkk 3d1336dc6c fix(ignore): update zigbee-herdsman to 0.33.7 (#21178)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-02 20:58:09 +00:00
koenkk da1d0d81c2 chore: fix release-please 2024-02-01 22:03:28 +01:00
koenkk 335e18fe98 chore: fix release-please 2024-02-01 22:02:01 +01:00
Koen Kanters 6e2539af09 chore: merge master to dev 2024-02-01 21:58:18 +01:00
koenkk fdc9f7d4d4 chore: fix dev -> master on release 2024-02-01 21:57:56 +01:00
Koen KantersandKoenkk 08c03e48cc fix(ignore): update zigbee-herdsman-converters to 18.26.0 (#21145)
Co-authored-by: Koenkk <2892853+Koenkk@users.noreply.github.com>
2024-02-01 20:35:49 +00:00
koenkk 6621f6fd91 chore: promote to dev 2024-02-01 17:45:05 +01:00
58 changed files with 3047 additions and 1965 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ blank_issues_enabled: false
contact_links:
- name: "IMPORTANT: Check development branch changelog first!!"
url: https://gist.github.com/Koenkk/bfd4c3d1725a2cccacc11d6ba51008ba
about: "Before submitting an issue, check if the issue hasn't already been solved in the development branch. Click 'Open' to see the release notes of the development branch. In case it is, you can read here how to switch to the development branch: https://www.zigbee2mqtt.io/advanced/more/switch-to-dev-branch.html"
about: "Before submitting an issue, check that the issue hasn't already been solved in the development branch. Click 'Open' to see the release notes of the development branch. In case it is, you can read here how to switch to the development branch: https://www.zigbee2mqtt.io/advanced/more/switch-to-dev-branch.html"
- name: Questions/discussion
url: https://github.com/Koenkk/zigbee2mqtt/discussions/new
about: Ask questions, discuss about devices or show things you made
+2 -2
View File
@@ -7,12 +7,12 @@ body:
attributes:
value: |
**IMPORTANT:** Before submitting:
- Is your feature request related to the fronted? Then click [here](https://github.com/nurikk/zigbee2mqtt-frontend/issues/new?assignees=&labels=&template=feature_request.md&title=)
- Is your feature request related to the frontend? Then click [here](https://github.com/nurikk/zigbee2mqtt-frontend/issues/new?assignees=&labels=&template=feature_request.md&title=)
- type: textarea
id: textarea1
attributes:
label: Is your feature request related to a problem? Please describe
placeholder: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
placeholder: A clear and concise description of what the problem is. Eg. I'm always frustrated when [...]
validations:
required: true
- type: textarea
@@ -36,7 +36,7 @@ body:
required: true
- type: textarea
attributes:
label: External defintion
label: External definition
description: See [Creating the external definition](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html#_2-creating-the-external-definition)
render: shell
validations:
+4 -3
View File
@@ -7,11 +7,12 @@ body:
value: |
**IMPORTANT:** Before submitting:
- You read the [FAQ](https://www.zigbee2mqtt.io/guide/faq/)
- Are you using an EZSP adapter (e.g. Dongle-E/SkyConnect)? Try the [new driver](https://github.com/Koenkk/zigbee2mqtt/discussions/21462)
- Zigbee2MQTT fails to start? Read [this](https://www.zigbee2mqtt.io/guide/installation/20_zigbee2mqtt-fails-to-start.html)
- Make sure the bug also occurs in the [dev branch](https://www.zigbee2mqtt.io/advanced/more/switch-to-dev-branch.html)
- Make sure you are using the [latest firmware](https://www.zigbee2mqtt.io/guide/adapters/#recommended) on your adapter
- The issue has not been [reported already](https://github.com/Koenkk/zigbee2mqtt/issues)
- Is your issue related to the fronted? Then click [here](https://github.com/nurikk/zigbee2mqtt-frontend/issues/new?assignees=&labels=bug%2Ctriage&template=bug_report.yaml&title=%5BBug%5D%3A+)
- Is your issue related to the frontend? Then click [here](https://github.com/nurikk/zigbee2mqtt-frontend/issues/new?assignees=&labels=bug%2Ctriage&template=bug_report.yaml&title=%5BBug%5D%3A+)
- If you are using the Home Assistant addon and are still on 1.18.1, check the first point of [this](https://github.com/Koenkk/zigbee2mqtt/releases/tag/1.19.0)
- type: textarea
id: what_happend
@@ -30,7 +31,7 @@ body:
id: reproduce
attributes:
label: How to reproduce it (minimal and precise)
placeholder: First do this, than this..
placeholder: First do this, then this..
validations:
required: false
- type: input
@@ -53,7 +54,7 @@ body:
id: adapter
attributes:
label: Adapter
description: The adapter you are using
description: The adapter you are using. In case of EZSP, try the [new `ember` driver](https://github.com/Koenkk/zigbee2mqtt/discussions/21462) first.
placeholder: Electrolama zig-a-zig-ah! (zzh!), Slaesh's CC2652RB stick, ...
validations:
required: true
+9 -2
View File
@@ -11,6 +11,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
if: (github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push'
with:
# Required for `release: merge dev -> master and promote dev`
token: ${{secrets.GH_TOKEN}}
- uses: actions/checkout@v4
if: ((github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push') == false
- uses: actions/setup-node@v4
with:
node-version: 20
@@ -96,9 +102,10 @@ jobs:
TAG=${GITHUB_REF#refs/*/}
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git fetch --unshallow
git fetch origin
git checkout master
git merge -m 'chore: merge dev to master' origin/dev
git merge --ff-only origin/dev
git push origin master
git checkout dev
jq ".version = \"$TAG-dev\"" package.json > package.json.tmp
@@ -113,7 +120,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node: [18, 20]
node: [18, 20, 22]
runs-on: ${{ matrix.os }}
continue-on-error: true
steps:
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
with:
node-version: 20
- uses: google-github-actions/release-please-action@v4
- uses: googleapis/release-please-action@v4
id: release
with:
target-branch: dev
+2 -2
View File
@@ -12,8 +12,8 @@ jobs:
- uses: actions/stale@v9
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days'
stale-pr-message: 'This pull request is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days'
stale-issue-message: 'This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 30 days'
stale-pr-message: 'This pull request is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 30 days'
days-before-stale: 180
days-before-close: 30
exempt-issue-labels: dont-stale
+2 -1
View File
@@ -23,7 +23,8 @@ jobs:
node-version: 20
cache: npm
# connect-gzip-static@4.0.0 requires Node 20 >=
- run: npx npm-check-updates -u -x connect-gzip-static
# eslint: https://github.com/typescript-eslint/typescript-eslint/issues/8211
- run: npx npm-check-updates -u -x connect-gzip-static -x eslint
- run: rm -f package-lock.json
- run: npm install
- uses: peter-evans/create-pull-request@v6
+2 -2
View File
@@ -1,3 +1,3 @@
{
".": "1.35.2"
}
".": "1.38.0"
}
+253
View File
@@ -1,5 +1,258 @@
# Changelog
## [1.38.0](https://github.com/Koenkk/zigbee2mqtt/compare/1.37.1...1.38.0) (2024-06-01)
### Features
* Add api for device re-interview ([#22788](https://github.com/Koenkk/zigbee2mqtt/issues/22788)) ([b6ad641](https://github.com/Koenkk/zigbee2mqtt/commit/b6ad6418a1cc9753092d1cfd6900d9d5b0b24c97))
### Bug Fixes
* Add namespace-specific levels support to logger ([#22619](https://github.com/Koenkk/zigbee2mqtt/issues/22619)) ([2eec6a4](https://github.com/Koenkk/zigbee2mqtt/commit/2eec6a4d26a69ed279206c7b988d56a9656ecaa2))
* Add Node 22 support, remove Node 21 https://github.com/Koenkk/zigbee2mqtt/issues/22595 ([cda867a](https://github.com/Koenkk/zigbee2mqtt/commit/cda867a38798f207fc9d2781535c93ab2728145d))
* Expose custom clusters to MQTT ([#22583](https://github.com/Koenkk/zigbee2mqtt/issues/22583)) ([d484405](https://github.com/Koenkk/zigbee2mqtt/commit/d484405cf099f384aa6888c2b8665b763433674a))
* Fix `Entity 'homeassistant/sensor' is unknown` https://github.com/Koenkk/zigbee2mqtt/issues/22258 ([63de2c5](https://github.com/Koenkk/zigbee2mqtt/commit/63de2c5ccc90c27c2dd4a4b4d7e87114b942fd5e))
* Fix custom cluster not available in frontend https://github.com/Koenkk/zigbee2mqtt/issues/22425 ([04e88ba](https://github.com/Koenkk/zigbee2mqtt/commit/04e88baf6076defe9abe59c8ea88265bde807163))
* **ignore:** Allow deprecated `warn` `log_level` https://github.com/Koenkk/zigbee2mqtt/issues/22706 ([b2d3265](https://github.com/Koenkk/zigbee2mqtt/commit/b2d32657f34e0636f1d37e06db7d07d49a1a0f89))
* **ignore:** Fix Home Assistant `device_automation` being cleared due to ef68cc328ec7780c55bb40072bfa0156cfb79d71 ([ae8a59d](https://github.com/Koenkk/zigbee2mqtt/commit/ae8a59dd51d4bf7d0af2fb0812b08b378fc6dabb))
* **ignore:** Revert "Update dependencies" ([#22753](https://github.com/Koenkk/zigbee2mqtt/issues/22753)) ([3b7392e](https://github.com/Koenkk/zigbee2mqtt/commit/3b7392ead2fd10beafa11f70fae77559fbdfe5b3))
* **ignore:** update dependencies ([#22752](https://github.com/Koenkk/zigbee2mqtt/issues/22752)) ([5ad7e15](https://github.com/Koenkk/zigbee2mqtt/commit/5ad7e1541fe8d5138a80f0c842a8b7f38c4f5c04))
* **ignore:** update zigbee-herdsman to 0.47.1 ([#22620](https://github.com/Koenkk/zigbee2mqtt/issues/22620)) ([0ba02d1](https://github.com/Koenkk/zigbee2mqtt/commit/0ba02d1f4004868865f8072509096d6be0319adb))
* **ignore:** update zigbee-herdsman to 0.47.2 ([#22640](https://github.com/Koenkk/zigbee2mqtt/issues/22640)) ([4f96581](https://github.com/Koenkk/zigbee2mqtt/commit/4f9658132b623813a6d170b56a0a1085bf070780))
* **ignore:** update zigbee-herdsman to 0.48.0 ([#22689](https://github.com/Koenkk/zigbee2mqtt/issues/22689)) ([e01662b](https://github.com/Koenkk/zigbee2mqtt/commit/e01662b3a21a5b6e1718400555e47cf95eb87f60))
* **ignore:** update zigbee-herdsman to 0.48.1 ([#22699](https://github.com/Koenkk/zigbee2mqtt/issues/22699)) ([91887c1](https://github.com/Koenkk/zigbee2mqtt/commit/91887c18772f63c3f616a0fbe531c6031c1307cb))
* **ignore:** update zigbee-herdsman to 0.49.0 ([#22754](https://github.com/Koenkk/zigbee2mqtt/issues/22754)) ([9b3a394](https://github.com/Koenkk/zigbee2mqtt/commit/9b3a39415f5868fabb4b1636ef8d3d5edd205a00))
* **ignore:** update zigbee-herdsman to 0.49.1 ([#22800](https://github.com/Koenkk/zigbee2mqtt/issues/22800)) ([0b3f4eb](https://github.com/Koenkk/zigbee2mqtt/commit/0b3f4eb0a776706ee6ee31cd3ab168a2351f8d18))
* **ignore:** update zigbee-herdsman to 0.49.2 ([#22864](https://github.com/Koenkk/zigbee2mqtt/issues/22864)) ([2cc68fd](https://github.com/Koenkk/zigbee2mqtt/commit/2cc68fd862f5f284d9c0cc1831b82b7b124e8655))
* **ignore:** update zigbee-herdsman-converters to 19.39.0 ([#22641](https://github.com/Koenkk/zigbee2mqtt/issues/22641)) ([2be947b](https://github.com/Koenkk/zigbee2mqtt/commit/2be947b29da3082f39356d84602eca9ba29c207c))
* **ignore:** update zigbee-herdsman-converters to 19.40.0 ([#22655](https://github.com/Koenkk/zigbee2mqtt/issues/22655)) ([2963397](https://github.com/Koenkk/zigbee2mqtt/commit/2963397b6056e4d91a62d5e66091be6f2ea23271))
* **ignore:** update zigbee-herdsman-converters to 19.41.0 ([#22690](https://github.com/Koenkk/zigbee2mqtt/issues/22690)) ([e9b7a84](https://github.com/Koenkk/zigbee2mqtt/commit/e9b7a847b01b8585f01171fa372e312f949504ce))
* **ignore:** update zigbee-herdsman-converters to 19.42.0 ([#22704](https://github.com/Koenkk/zigbee2mqtt/issues/22704)) ([75608c7](https://github.com/Koenkk/zigbee2mqtt/commit/75608c79c82337e62698a8587880be566cbabccc))
* **ignore:** update zigbee-herdsman-converters to 19.43.0 ([#22730](https://github.com/Koenkk/zigbee2mqtt/issues/22730)) ([30f6c8e](https://github.com/Koenkk/zigbee2mqtt/commit/30f6c8e60c66deae5187b906df8ce9997fbb10a1))
* **ignore:** update zigbee-herdsman-converters to 19.44.0 ([#22801](https://github.com/Koenkk/zigbee2mqtt/issues/22801)) ([9f5b5d1](https://github.com/Koenkk/zigbee2mqtt/commit/9f5b5d1a76914f59d64075c34ffcee9e488c2ca4))
* **ignore:** update zigbee-herdsman-converters to 19.46.0 ([#22842](https://github.com/Koenkk/zigbee2mqtt/issues/22842)) ([1a12c2f](https://github.com/Koenkk/zigbee2mqtt/commit/1a12c2f15e26baeb22e61bd36bac275f3a68cd32))
* **ignore:** update zigbee-herdsman-converters to 19.47.0 ([#22855](https://github.com/Koenkk/zigbee2mqtt/issues/22855)) ([a02fd3c](https://github.com/Koenkk/zigbee2mqtt/commit/a02fd3cee640d1b3cbd3dc9e1d837b7dae71cd8a))
* **ignore:** update zigbee-herdsman-converters to 19.47.1 ([#22865](https://github.com/Koenkk/zigbee2mqtt/issues/22865)) ([33d8223](https://github.com/Koenkk/zigbee2mqtt/commit/33d8223e529d27cca22a935dc8a89092aee8a4d3))
* **ignore:** update zigbee2mqtt-frontend to 0.6.168 ([#22698](https://github.com/Koenkk/zigbee2mqtt/issues/22698)) ([8ab3c46](https://github.com/Koenkk/zigbee2mqtt/commit/8ab3c464dce4db6d3ce25f61f40c64f58c568d6a))
* **ignore:** update zigbee2mqtt-frontend to 0.7.0 ([#22761](https://github.com/Koenkk/zigbee2mqtt/issues/22761)) ([b984a2d](https://github.com/Koenkk/zigbee2mqtt/commit/b984a2d0926a40572da60a5e900420fff9387a1b))
* **ignore:** update zigbee2mqtt-frontend to 0.7.1 ([#22854](https://github.com/Koenkk/zigbee2mqtt/issues/22854)) ([a3ea91d](https://github.com/Koenkk/zigbee2mqtt/commit/a3ea91deb981bdc56d03bc09ff6c116dff9f7eee))
* Improve settings changing ([#22674](https://github.com/Koenkk/zigbee2mqtt/issues/22674)) ([ce6043a](https://github.com/Koenkk/zigbee2mqtt/commit/ce6043acb0aa5c9a029cc5721b6359bbcae59fca))
* Optimize Home Assistant discovery ([#22701](https://github.com/Koenkk/zigbee2mqtt/issues/22701)) ([ef68cc3](https://github.com/Koenkk/zigbee2mqtt/commit/ef68cc328ec7780c55bb40072bfa0156cfb79d71))
## [1.37.1](https://github.com/Koenkk/zigbee2mqtt/compare/1.37.0...1.37.1) (2024-05-09)
### Bug Fixes
* Add Helm chart ([#21852](https://github.com/Koenkk/zigbee2mqtt/issues/21852)) ([b2e8b3e](https://github.com/Koenkk/zigbee2mqtt/commit/b2e8b3e00c152540109e7b9101784f3b03261b40))
* Add settings for finer control over debug log level ([#22426](https://github.com/Koenkk/zigbee2mqtt/issues/22426)) ([eb49f52](https://github.com/Koenkk/zigbee2mqtt/commit/eb49f523bf3a0c085655cfae7aac97d7da749db9))
* Fix logger crashing on restart ([#22565](https://github.com/Koenkk/zigbee2mqtt/issues/22565)) ([d26ccae](https://github.com/Koenkk/zigbee2mqtt/commit/d26ccaed65b922b00f1a7599941289ead4b7fb31))
* Fix MQTT `ECONNREFUSED error` https://github.com/zigbee2mqtt/hassio-zigbee2mqtt/issues/605 ([a0c9a17](https://github.com/Koenkk/zigbee2mqtt/commit/a0c9a1769f7e25673672e3a312b2e4bd3812ff32))
* **ignore:** update dependencies ([#22456](https://github.com/Koenkk/zigbee2mqtt/issues/22456)) ([b754b9d](https://github.com/Koenkk/zigbee2mqtt/commit/b754b9d4635ef3300893acba51eadae4e41039a3))
* **ignore:** update zigbee-herdsman to 0.46.0 ([#22380](https://github.com/Koenkk/zigbee2mqtt/issues/22380)) ([55b5f91](https://github.com/Koenkk/zigbee2mqtt/commit/55b5f91a96f589fd0e3da5f4800dad32dbd57cf7))
* **ignore:** update zigbee-herdsman to 0.46.1 ([#22439](https://github.com/Koenkk/zigbee2mqtt/issues/22439)) ([329d832](https://github.com/Koenkk/zigbee2mqtt/commit/329d832337cda15b324c46a2adf6a24c5d38a8bd))
* **ignore:** update zigbee-herdsman to 0.46.2 ([#22470](https://github.com/Koenkk/zigbee2mqtt/issues/22470)) ([e4c9bac](https://github.com/Koenkk/zigbee2mqtt/commit/e4c9bac348680f5571cef0b79407fb9459757a36))
* **ignore:** update zigbee-herdsman to 0.46.3 ([#22471](https://github.com/Koenkk/zigbee2mqtt/issues/22471)) ([316654b](https://github.com/Koenkk/zigbee2mqtt/commit/316654b5d3acc6452e888d0e48a77908c431e783))
* **ignore:** update zigbee-herdsman to 0.46.4 ([#22501](https://github.com/Koenkk/zigbee2mqtt/issues/22501)) ([e44608a](https://github.com/Koenkk/zigbee2mqtt/commit/e44608a46dfe3baab1f82dd430f66afe0ac09c4f))
* **ignore:** update zigbee-herdsman to 0.46.5 ([#22552](https://github.com/Koenkk/zigbee2mqtt/issues/22552)) ([6bcb4b6](https://github.com/Koenkk/zigbee2mqtt/commit/6bcb4b66b1a69f19b49a3b9a4c9be32e8f1df4e4))
* **ignore:** update zigbee-herdsman to 0.46.6 ([#22558](https://github.com/Koenkk/zigbee2mqtt/issues/22558)) ([989f667](https://github.com/Koenkk/zigbee2mqtt/commit/989f667c81e4f2b61edd379382d2c8f77c556e3b))
* **ignore:** update zigbee-herdsman-converters to 19.33.0 ([#22381](https://github.com/Koenkk/zigbee2mqtt/issues/22381)) ([907591c](https://github.com/Koenkk/zigbee2mqtt/commit/907591c467f926af172427ff641e7b20e5fa309c))
* **ignore:** update zigbee-herdsman-converters to 19.35.0 ([#22442](https://github.com/Koenkk/zigbee2mqtt/issues/22442)) ([beedfae](https://github.com/Koenkk/zigbee2mqtt/commit/beedfaefacdb53d3c379164dbb4dae5de396d40d))
* **ignore:** update zigbee-herdsman-converters to 19.36.0 ([#22472](https://github.com/Koenkk/zigbee2mqtt/issues/22472)) ([358e2ee](https://github.com/Koenkk/zigbee2mqtt/commit/358e2eee4551baee58c4d212d9d65562d2e84bd6))
* **ignore:** update zigbee-herdsman-converters to 19.36.1 ([#22503](https://github.com/Koenkk/zigbee2mqtt/issues/22503)) ([3544d51](https://github.com/Koenkk/zigbee2mqtt/commit/3544d5172e03cdcbd61cde13ebc20c2e5fbeaba5))
* **ignore:** update zigbee-herdsman-converters to 19.37.0 ([#22531](https://github.com/Koenkk/zigbee2mqtt/issues/22531)) ([6b886f6](https://github.com/Koenkk/zigbee2mqtt/commit/6b886f626863cae8acc2895a7fe2482b068138d6))
* **ignore:** update zigbee-herdsman-converters to 19.37.1 ([#22549](https://github.com/Koenkk/zigbee2mqtt/issues/22549)) ([cc9a95c](https://github.com/Koenkk/zigbee2mqtt/commit/cc9a95c74e53261636dc7329be2becddf4772fdc))
* **ignore:** update zigbee-herdsman-converters to 19.37.2 ([#22570](https://github.com/Koenkk/zigbee2mqtt/issues/22570)) ([06fd8a4](https://github.com/Koenkk/zigbee2mqtt/commit/06fd8a4eb120f03dd9f3a1d59ab7b20a32fe15a9))
* **ignore:** update zigbee2mqtt-frontend to 0.6.166 ([#22410](https://github.com/Koenkk/zigbee2mqtt/issues/22410)) ([5899436](https://github.com/Koenkk/zigbee2mqtt/commit/5899436d74cb0c67016758797c2e928f35715d9d))
* **ignore:** update zigbee2mqtt-frontend to 0.6.167 ([#22419](https://github.com/Koenkk/zigbee2mqtt/issues/22419)) ([23e97d0](https://github.com/Koenkk/zigbee2mqtt/commit/23e97d0001048269662c6336a8aef6788615934c))
## [1.37.0](https://github.com/Koenkk/zigbee2mqtt/compare/1.36.1...1.37.0) (2024-05-01)
### Features
* Unified logging across z2m/zh/zhc ([#21984](https://github.com/Koenkk/zigbee2mqtt/issues/21984)) ([73566df](https://github.com/Koenkk/zigbee2mqtt/commit/73566dff98e64038117ec71c7aaa897a4070474d))
### Bug Fixes
* Change `MQTT publish` log to `debug` ([#22063](https://github.com/Koenkk/zigbee2mqtt/issues/22063)) ([bbb6f9e](https://github.com/Koenkk/zigbee2mqtt/commit/bbb6f9e042a5c6cce78591e402d8ab0f7ab4195e))
* Cleanup old logger leftovers ([#22069](https://github.com/Koenkk/zigbee2mqtt/issues/22069)) ([919c4ee](https://github.com/Koenkk/zigbee2mqtt/commit/919c4eee739d9c2dee855cf868e88f7848e6af36))
* Convert logger into a class ([#22129](https://github.com/Koenkk/zigbee2mqtt/issues/22129)) ([3c1a4af](https://github.com/Koenkk/zigbee2mqtt/commit/3c1a4afcb723eecccf6ab9c3e04e1e0eca7b2cbb))
* Disable automatic reconfigure when configureKey changes ([#22088](https://github.com/Koenkk/zigbee2mqtt/issues/22088)) ([9d251db](https://github.com/Koenkk/zigbee2mqtt/commit/9d251dbad5660f7663a79360a584df69d6f10875))
* Fix crash when stopping in case MQTT client did not connect yet ([#22110](https://github.com/Koenkk/zigbee2mqtt/issues/22110)) ([92f5323](https://github.com/Koenkk/zigbee2mqtt/commit/92f53235f3e143ade8883dce01439f87913e2679))
* **ignore:** fix 73566df ([cdd6fb0](https://github.com/Koenkk/zigbee2mqtt/commit/cdd6fb086419892e2725fc545b8015d83dea145d))
* **ignore:** fix 73566dff98e64038117ec71c7aaa897a4070474d ([780c065](https://github.com/Koenkk/zigbee2mqtt/commit/780c065fa106358683bbef6fd72b0257adca5bae))
* **ignore:** update dependencies ([#22007](https://github.com/Koenkk/zigbee2mqtt/issues/22007)) ([e748718](https://github.com/Koenkk/zigbee2mqtt/commit/e7487189cf6b2143a5606ce9bac7d4fa6e240587))
* **ignore:** update dependencies ([#22331](https://github.com/Koenkk/zigbee2mqtt/issues/22331)) ([d7e2d98](https://github.com/Koenkk/zigbee2mqtt/commit/d7e2d98a5a79689cfdc06e1f9bd3b1ed10251132))
* **ignore:** update zigbee-herdsman to 0.41.0 ([#22045](https://github.com/Koenkk/zigbee2mqtt/issues/22045)) ([bd0a411](https://github.com/Koenkk/zigbee2mqtt/commit/bd0a41107a18ab725d0d669af7993f2ad57cd9dd))
* **ignore:** update zigbee-herdsman to 0.41.1 ([#22064](https://github.com/Koenkk/zigbee2mqtt/issues/22064)) ([3e91349](https://github.com/Koenkk/zigbee2mqtt/commit/3e913495b1a843cf0d67e4f2ffba975f56dd5f94))
* **ignore:** update zigbee-herdsman to 0.41.2 ([#22087](https://github.com/Koenkk/zigbee2mqtt/issues/22087)) ([a020138](https://github.com/Koenkk/zigbee2mqtt/commit/a020138f6b4e90377d8bacf475fd33f62abb24e5))
* **ignore:** update zigbee-herdsman to 0.42.0 ([#22121](https://github.com/Koenkk/zigbee2mqtt/issues/22121)) ([28b8aff](https://github.com/Koenkk/zigbee2mqtt/commit/28b8aff1ae8cabce8f61e34c85cd0b72b52a1e83))
* **ignore:** update zigbee-herdsman to 0.42.1 ([#22143](https://github.com/Koenkk/zigbee2mqtt/issues/22143)) ([a210c23](https://github.com/Koenkk/zigbee2mqtt/commit/a210c238f1156bdbb56ba5fc223d0b2159c07273))
* **ignore:** update zigbee-herdsman to 0.42.2 ([#22220](https://github.com/Koenkk/zigbee2mqtt/issues/22220)) ([76f5425](https://github.com/Koenkk/zigbee2mqtt/commit/76f54254529d13bfa0e9a40a0c7455b0c1e61bc8))
* **ignore:** update zigbee-herdsman to 0.42.3 ([#22253](https://github.com/Koenkk/zigbee2mqtt/issues/22253)) ([f7abbd5](https://github.com/Koenkk/zigbee2mqtt/commit/f7abbd5f5ab878bea364cc0cda4222b8941fd487))
* **ignore:** update zigbee-herdsman to 0.42.4 ([#22283](https://github.com/Koenkk/zigbee2mqtt/issues/22283)) ([a8f6bec](https://github.com/Koenkk/zigbee2mqtt/commit/a8f6bec673f43fd76c3495927c2c44e831ade6da))
* **ignore:** update zigbee-herdsman to 0.42.5 ([#22285](https://github.com/Koenkk/zigbee2mqtt/issues/22285)) ([4f97a7a](https://github.com/Koenkk/zigbee2mqtt/commit/4f97a7ad2c12ec70ff459dc233d55bada734e7ab))
* **ignore:** update zigbee-herdsman to 0.43.0 ([#22300](https://github.com/Koenkk/zigbee2mqtt/issues/22300)) ([75f9743](https://github.com/Koenkk/zigbee2mqtt/commit/75f9743528ac3f55b837548f234b29bdf8182a0d))
* **ignore:** update zigbee-herdsman to 0.43.1 ([#22317](https://github.com/Koenkk/zigbee2mqtt/issues/22317)) ([389779d](https://github.com/Koenkk/zigbee2mqtt/commit/389779d4e332db1945ff7b490ec881eec71b4721))
* **ignore:** update zigbee-herdsman to 0.44.0 ([#22339](https://github.com/Koenkk/zigbee2mqtt/issues/22339)) ([1b089ef](https://github.com/Koenkk/zigbee2mqtt/commit/1b089ef6ae364786602c485ea5deafad4e21f023))
* **ignore:** update zigbee-herdsman to 0.45.0 ([#22365](https://github.com/Koenkk/zigbee2mqtt/issues/22365)) ([8e27ec2](https://github.com/Koenkk/zigbee2mqtt/commit/8e27ec24f664ca702eba93f90b42c9f4e74d16df))
* **ignore:** update zigbee-herdsman-converters to 19.13.0 ([#22046](https://github.com/Koenkk/zigbee2mqtt/issues/22046)) ([db7d9e1](https://github.com/Koenkk/zigbee2mqtt/commit/db7d9e1120e01dd6b4e20bd352689eebb9a8912f))
* **ignore:** update zigbee-herdsman-converters to 19.15.0 ([#22089](https://github.com/Koenkk/zigbee2mqtt/issues/22089)) ([ae35aae](https://github.com/Koenkk/zigbee2mqtt/commit/ae35aaed5ef3591f288019d9cb35d90a31aa4798))
* **ignore:** update zigbee-herdsman-converters to 19.16.0 ([#22107](https://github.com/Koenkk/zigbee2mqtt/issues/22107)) ([394a3fb](https://github.com/Koenkk/zigbee2mqtt/commit/394a3fbd8aeb1dd4708326d8f854ac7db4b28dbf))
* **ignore:** update zigbee-herdsman-converters to 19.17.0 ([#22122](https://github.com/Koenkk/zigbee2mqtt/issues/22122)) ([cabc9f5](https://github.com/Koenkk/zigbee2mqtt/commit/cabc9f5e2f6e22fb380e259093d29ec506a3b1f3))
* **ignore:** update zigbee-herdsman-converters to 19.18.0 ([#22141](https://github.com/Koenkk/zigbee2mqtt/issues/22141)) ([8b8d660](https://github.com/Koenkk/zigbee2mqtt/commit/8b8d660b0622072696113c3a66458cbde1918838))
* **ignore:** update zigbee-herdsman-converters to 19.19.0 ([#22153](https://github.com/Koenkk/zigbee2mqtt/issues/22153)) ([cdbbe0d](https://github.com/Koenkk/zigbee2mqtt/commit/cdbbe0d49fff21a3fb5000ecef25bb71b3d64360))
* **ignore:** update zigbee-herdsman-converters to 19.19.1 ([#22171](https://github.com/Koenkk/zigbee2mqtt/issues/22171)) ([f774753](https://github.com/Koenkk/zigbee2mqtt/commit/f77475321300a11ca0d30525fe38afa7d079f42e))
* **ignore:** update zigbee-herdsman-converters to 19.20.0 ([#22187](https://github.com/Koenkk/zigbee2mqtt/issues/22187)) ([e10ab5c](https://github.com/Koenkk/zigbee2mqtt/commit/e10ab5ccb272fdc3b2922fd28c043d84860137e1))
* **ignore:** update zigbee-herdsman-converters to 19.21.0 ([#22199](https://github.com/Koenkk/zigbee2mqtt/issues/22199)) ([87aa18d](https://github.com/Koenkk/zigbee2mqtt/commit/87aa18d14cbf19a89d7d0dced6eb86393f0239af))
* **ignore:** update zigbee-herdsman-converters to 19.22.0 ([#22221](https://github.com/Koenkk/zigbee2mqtt/issues/22221)) ([172aa74](https://github.com/Koenkk/zigbee2mqtt/commit/172aa7461ac507aa55dcd653d40873445c827f28))
* **ignore:** update zigbee-herdsman-converters to 19.23.0 ([#22232](https://github.com/Koenkk/zigbee2mqtt/issues/22232)) ([1544317](https://github.com/Koenkk/zigbee2mqtt/commit/1544317fce5de3be71395af06450a5e997b39dab))
* **ignore:** update zigbee-herdsman-converters to 19.24.0 ([#22254](https://github.com/Koenkk/zigbee2mqtt/issues/22254)) ([d9628df](https://github.com/Koenkk/zigbee2mqtt/commit/d9628df17b228a0908d7b95e35cbb97171c2485c))
* **ignore:** update zigbee-herdsman-converters to 19.25.0 ([#22266](https://github.com/Koenkk/zigbee2mqtt/issues/22266)) ([80504b9](https://github.com/Koenkk/zigbee2mqtt/commit/80504b943d742a20481b458eb0fdc9cdf9452961))
* **ignore:** update zigbee-herdsman-converters to 19.26.0 ([#22284](https://github.com/Koenkk/zigbee2mqtt/issues/22284)) ([4bf2ffa](https://github.com/Koenkk/zigbee2mqtt/commit/4bf2ffa7ddd0de3d70160ccefbaf483fc9a83a22))
* **ignore:** update zigbee-herdsman-converters to 19.27.0 ([#22301](https://github.com/Koenkk/zigbee2mqtt/issues/22301)) ([df23240](https://github.com/Koenkk/zigbee2mqtt/commit/df232406838b991c87ddbaa6c28ebcedd6226fcf))
* **ignore:** update zigbee-herdsman-converters to 19.28.0 ([#22318](https://github.com/Koenkk/zigbee2mqtt/issues/22318)) ([245afb1](https://github.com/Koenkk/zigbee2mqtt/commit/245afb112648c23c74c8e1e9d9b038c84d71ca40))
* **ignore:** update zigbee-herdsman-converters to 19.29.0 ([#22325](https://github.com/Koenkk/zigbee2mqtt/issues/22325)) ([3755126](https://github.com/Koenkk/zigbee2mqtt/commit/37551265a204b6509b2534b5b0fcc9825b5188ac))
* **ignore:** update zigbee-herdsman-converters to 19.32.0 ([#22377](https://github.com/Koenkk/zigbee2mqtt/issues/22377)) ([e03013c](https://github.com/Koenkk/zigbee2mqtt/commit/e03013c58180391d9f3fe6878712ab032896a94f))
* **ignore:** update zigbee2mqtt-frontend to 0.6.162 ([#22094](https://github.com/Koenkk/zigbee2mqtt/issues/22094)) ([0c80129](https://github.com/Koenkk/zigbee2mqtt/commit/0c801294661b0f6e592dd09f681c83bff376414d))
* **ignore:** update zigbee2mqtt-frontend to 0.6.163 ([#22288](https://github.com/Koenkk/zigbee2mqtt/issues/22288)) ([e5d632b](https://github.com/Koenkk/zigbee2mqtt/commit/e5d632bc9177c134d6856f9b5881f26be4ed4140))
* **ignore:** update zigbee2mqtt-frontend to 0.6.164 ([#22340](https://github.com/Koenkk/zigbee2mqtt/issues/22340)) ([10e6ad0](https://github.com/Koenkk/zigbee2mqtt/commit/10e6ad0e67a237693365c5bc8752ba3cbefe757f))
* **ignore:** update zigbee2mqtt-frontend to 0.6.165 ([#22366](https://github.com/Koenkk/zigbee2mqtt/issues/22366)) ([8a288a9](https://github.com/Koenkk/zigbee2mqtt/commit/8a288a91192bfa47aad80435a02cf3313da0b4bc))
* Reduce MQTT publishing by excluding debug logging from `bridge/logging` ([#22066](https://github.com/Koenkk/zigbee2mqtt/issues/22066)) ([002050f](https://github.com/Koenkk/zigbee2mqtt/commit/002050f35bd6228a464482d17a7feee82f9f5d31))
* Remove deprecated Home Assistant `color_mode` ([#22142](https://github.com/Koenkk/zigbee2mqtt/issues/22142)) ([c98e5a7](https://github.com/Koenkk/zigbee2mqtt/commit/c98e5a74de1c322533f9c0defa0bbf0b5627b775))
## [1.36.1](https://github.com/Koenkk/zigbee2mqtt/compare/1.36.0...1.36.1) (2024-04-01)
### Bug Fixes
* Add more Home Assistant entity icons ([#21652](https://github.com/Koenkk/zigbee2mqtt/issues/21652)) ([01843fe](https://github.com/Koenkk/zigbee2mqtt/commit/01843fe6c43210ade1d1dfcd134fdd979d47f194))
* Fix `set` endpoint name conversion ([#21653](https://github.com/Koenkk/zigbee2mqtt/issues/21653)) ([66fb672](https://github.com/Koenkk/zigbee2mqtt/commit/66fb672492c8f049368d5dbda3925500941cd534))
* Fix crash due to race condition during interview ([#21680](https://github.com/Koenkk/zigbee2mqtt/issues/21680)) ([2da8095](https://github.com/Koenkk/zigbee2mqtt/commit/2da80954d69c753bcfbef30c2579584d7a0f8779))
* Fix incorrect update state when Z2M is restarted during OTA update https://github.com/Koenkk/zigbee2mqtt/issues/12794 ([1110d3f](https://github.com/Koenkk/zigbee2mqtt/commit/1110d3f8efff948811a980968f8dc84465500d4f))
* Home Assistant: remove button name if `device_class` is present ([#21726](https://github.com/Koenkk/zigbee2mqtt/issues/21726)) ([14e0440](https://github.com/Koenkk/zigbee2mqtt/commit/14e044093c4f481fc7c4f58c35c68ebddddf621c))
* **ignore:** fix tests ([29543ba](https://github.com/Koenkk/zigbee2mqtt/commit/29543bad4bb8768bb66c15f4f084b0d9ccc6c1bc))
* **ignore:** update dependencies ([#21657](https://github.com/Koenkk/zigbee2mqtt/issues/21657)) ([8c86849](https://github.com/Koenkk/zigbee2mqtt/commit/8c86849bb992e1125e9fed301eef8655aa95466c))
* **ignore:** Update dependencies ([#21740](https://github.com/Koenkk/zigbee2mqtt/issues/21740)) ([43610bb](https://github.com/Koenkk/zigbee2mqtt/commit/43610bbff44572937650ee03ccd239d084d9959f))
* **ignore:** update dependencies ([#21832](https://github.com/Koenkk/zigbee2mqtt/issues/21832)) ([1006725](https://github.com/Koenkk/zigbee2mqtt/commit/1006725b09a6e07e60586409798de3cbc7f3c1f1))
* **ignore:** update dependencies ([#21923](https://github.com/Koenkk/zigbee2mqtt/issues/21923)) ([8c4cd8c](https://github.com/Koenkk/zigbee2mqtt/commit/8c4cd8c9aa8777027d538a114fc9310b1f2fe7a2))
* **ignore:** Update manufacturer codes ([#21729](https://github.com/Koenkk/zigbee2mqtt/issues/21729)) ([83502f2](https://github.com/Koenkk/zigbee2mqtt/commit/83502f2d30cf2d79f3ea8576ab065590da29abd8))
* **ignore:** update zigbee-herdsman to 0.35.2 ([#21654](https://github.com/Koenkk/zigbee2mqtt/issues/21654)) ([ccaa128](https://github.com/Koenkk/zigbee2mqtt/commit/ccaa1281a35249874a065fd2d4975aeab93f05f4))
* **ignore:** update zigbee-herdsman to 0.35.3 ([#21677](https://github.com/Koenkk/zigbee2mqtt/issues/21677)) ([30909d5](https://github.com/Koenkk/zigbee2mqtt/commit/30909d57fba7f7a45caeb14afcbf5f8ac724c876))
* **ignore:** update zigbee-herdsman to 0.36.0 ([#21689](https://github.com/Koenkk/zigbee2mqtt/issues/21689)) ([1d73d9d](https://github.com/Koenkk/zigbee2mqtt/commit/1d73d9d4768d8511ca24066fe199d4096b0301ac))
* **ignore:** update zigbee-herdsman to 0.36.1 ([#21713](https://github.com/Koenkk/zigbee2mqtt/issues/21713)) ([e834e93](https://github.com/Koenkk/zigbee2mqtt/commit/e834e93debbd343236e71e275bbd3726a3b4ae3c))
* **ignore:** update zigbee-herdsman to 0.36.2 ([#21727](https://github.com/Koenkk/zigbee2mqtt/issues/21727)) ([ebf996a](https://github.com/Koenkk/zigbee2mqtt/commit/ebf996acd83f32668be7dfd3994c2976db52bc58))
* **ignore:** update zigbee-herdsman to 0.37.0 ([#21743](https://github.com/Koenkk/zigbee2mqtt/issues/21743)) ([b60f679](https://github.com/Koenkk/zigbee2mqtt/commit/b60f679814294ce454e4e7c08fc3dd11b9a224ff))
* **ignore:** update zigbee-herdsman to 0.38.0 ([#21777](https://github.com/Koenkk/zigbee2mqtt/issues/21777)) ([233d263](https://github.com/Koenkk/zigbee2mqtt/commit/233d263e132e7b046194dedd620ae687a815f1ec))
* **ignore:** update zigbee-herdsman to 0.38.1 ([#21802](https://github.com/Koenkk/zigbee2mqtt/issues/21802)) ([6586228](https://github.com/Koenkk/zigbee2mqtt/commit/658622880ffc7eee1c27ba499ce75da03b25c216))
* **ignore:** update zigbee-herdsman to 0.39.0 ([#21871](https://github.com/Koenkk/zigbee2mqtt/issues/21871)) ([801134b](https://github.com/Koenkk/zigbee2mqtt/commit/801134b6303ecea0d41254ac84a6f0f96393dc3f))
* **ignore:** update zigbee-herdsman to 0.39.1 ([#21891](https://github.com/Koenkk/zigbee2mqtt/issues/21891)) ([f05d501](https://github.com/Koenkk/zigbee2mqtt/commit/f05d501380a5416fb610f6eeeea7c9b7a04394cb))
* **ignore:** update zigbee-herdsman to 0.40.0 ([#21901](https://github.com/Koenkk/zigbee2mqtt/issues/21901)) ([334e260](https://github.com/Koenkk/zigbee2mqtt/commit/334e260b851535379548e52a61cbef5956411f46))
* **ignore:** update zigbee-herdsman to 0.40.1 ([#21987](https://github.com/Koenkk/zigbee2mqtt/issues/21987)) ([0c7730f](https://github.com/Koenkk/zigbee2mqtt/commit/0c7730f4c50fb97313de35533140a8c99f6ad374))
* **ignore:** update zigbee-herdsman to 0.40.2 ([#22000](https://github.com/Koenkk/zigbee2mqtt/issues/22000)) ([886b534](https://github.com/Koenkk/zigbee2mqtt/commit/886b5345d7c134f27aa14e259ce1cfd8423b8636))
* **ignore:** update zigbee-herdsman to 0.40.3 ([#22010](https://github.com/Koenkk/zigbee2mqtt/issues/22010)) ([1e74b2f](https://github.com/Koenkk/zigbee2mqtt/commit/1e74b2f53ff7af8d1067265c966f75aef8a794f1))
* **ignore:** update zigbee-herdsman-converters to 18.44.0 ([#21690](https://github.com/Koenkk/zigbee2mqtt/issues/21690)) ([6daa7aa](https://github.com/Koenkk/zigbee2mqtt/commit/6daa7aa61db7c6ceef707ab27afca14e31a2e79f))
* **ignore:** update zigbee-herdsman-converters to 18.45.0 ([#21699](https://github.com/Koenkk/zigbee2mqtt/issues/21699)) ([d37c164](https://github.com/Koenkk/zigbee2mqtt/commit/d37c164993cd4b3bc2f027be3a3a2b9a5b30d200))
* **ignore:** update zigbee-herdsman-converters to 18.46.0 ([#21714](https://github.com/Koenkk/zigbee2mqtt/issues/21714)) ([0e0eccb](https://github.com/Koenkk/zigbee2mqtt/commit/0e0eccbd139e38df31b0860af26c50032f629fae))
* **ignore:** update zigbee-herdsman-converters to 18.47.0 ([#21728](https://github.com/Koenkk/zigbee2mqtt/issues/21728)) ([3188038](https://github.com/Koenkk/zigbee2mqtt/commit/3188038a7bf673b2053783910229e1e60b6f5921))
* **ignore:** update zigbee-herdsman-converters to 18.48.0 ([#21753](https://github.com/Koenkk/zigbee2mqtt/issues/21753)) ([2a047a4](https://github.com/Koenkk/zigbee2mqtt/commit/2a047a469c70f17a3c12d0a6f6fc2de9744835e7))
* **ignore:** update zigbee-herdsman-converters to 19.0.0 ([#21768](https://github.com/Koenkk/zigbee2mqtt/issues/21768)) ([dbd616f](https://github.com/Koenkk/zigbee2mqtt/commit/dbd616fbe5a62986d2c28bbcc9cea93d293da397))
* **ignore:** update zigbee-herdsman-converters to 19.1.0 ([#21778](https://github.com/Koenkk/zigbee2mqtt/issues/21778)) ([56feb77](https://github.com/Koenkk/zigbee2mqtt/commit/56feb77f2d1c357d59245db3b7cc37520554ed41))
* **ignore:** update zigbee-herdsman-converters to 19.1.1 ([#21789](https://github.com/Koenkk/zigbee2mqtt/issues/21789)) ([720fc49](https://github.com/Koenkk/zigbee2mqtt/commit/720fc491ce70f197778b5abe750ac2d779a9ad4c))
* **ignore:** update zigbee-herdsman-converters to 19.10.0 ([#21996](https://github.com/Koenkk/zigbee2mqtt/issues/21996)) ([7d8b537](https://github.com/Koenkk/zigbee2mqtt/commit/7d8b5374adca3c97976e557f1d44e1b85728d575))
* **ignore:** update zigbee-herdsman-converters to 19.11.0 ([#22013](https://github.com/Koenkk/zigbee2mqtt/issues/22013)) ([e51792c](https://github.com/Koenkk/zigbee2mqtt/commit/e51792c65afa5a2bc863bb619c0057fc7242b191))
* **ignore:** update zigbee-herdsman-converters to 19.11.1 ([#22014](https://github.com/Koenkk/zigbee2mqtt/issues/22014)) ([85c00b8](https://github.com/Koenkk/zigbee2mqtt/commit/85c00b8b43e2716231203599fac8bf01662535d5))
* **ignore:** update zigbee-herdsman-converters to 19.11.2 ([#22021](https://github.com/Koenkk/zigbee2mqtt/issues/22021)) ([3aadab0](https://github.com/Koenkk/zigbee2mqtt/commit/3aadab0925d52839baf6d884ea59d25ff083a015))
* **ignore:** update zigbee-herdsman-converters to 19.2.1 ([#21801](https://github.com/Koenkk/zigbee2mqtt/issues/21801)) ([a4f9c49](https://github.com/Koenkk/zigbee2mqtt/commit/a4f9c49ab5ef647829b8f505df7f91df8fca5036))
* **ignore:** update zigbee-herdsman-converters to 19.3.0 ([#21851](https://github.com/Koenkk/zigbee2mqtt/issues/21851)) ([1a9e05e](https://github.com/Koenkk/zigbee2mqtt/commit/1a9e05e6ab93a1e93024600a58c5b8e62544daf4))
* **ignore:** update zigbee-herdsman-converters to 19.4.0 ([#21870](https://github.com/Koenkk/zigbee2mqtt/issues/21870)) ([73d4300](https://github.com/Koenkk/zigbee2mqtt/commit/73d43007d45ee3cfc2d49ff2f4475fef63560421))
* **ignore:** update zigbee-herdsman-converters to 19.4.1 ([#21872](https://github.com/Koenkk/zigbee2mqtt/issues/21872)) ([b02c34c](https://github.com/Koenkk/zigbee2mqtt/commit/b02c34c6e52cf97364f6affce82b281cd40c1780))
* **ignore:** update zigbee-herdsman-converters to 19.4.2 ([#21892](https://github.com/Koenkk/zigbee2mqtt/issues/21892)) ([25cec20](https://github.com/Koenkk/zigbee2mqtt/commit/25cec20f0d0b6201679b719bf8dec9e7ebb26b62))
* **ignore:** update zigbee-herdsman-converters to 19.5.0 ([#21902](https://github.com/Koenkk/zigbee2mqtt/issues/21902)) ([73423eb](https://github.com/Koenkk/zigbee2mqtt/commit/73423ebd60a04f0caf7ac40743422cea3ef052e9))
* **ignore:** update zigbee-herdsman-converters to 19.6.0 ([#21927](https://github.com/Koenkk/zigbee2mqtt/issues/21927)) ([32b173c](https://github.com/Koenkk/zigbee2mqtt/commit/32b173c6537a4cdc3c303e521c5fa159cfcfb58d))
* **ignore:** update zigbee-herdsman-converters to 19.8.0 ([#21957](https://github.com/Koenkk/zigbee2mqtt/issues/21957)) ([032b9a0](https://github.com/Koenkk/zigbee2mqtt/commit/032b9a096eac7ba01a8eee188c042287db55bfed))
* **ignore:** update zigbee-herdsman-converters to 19.9.0 ([#21970](https://github.com/Koenkk/zigbee2mqtt/issues/21970)) ([a7aab71](https://github.com/Koenkk/zigbee2mqtt/commit/a7aab714ba1861eab15d0df3228e222feb8fa996))
* **ignore:** update zigbee2mqtt-frontend to 0.6.161 ([#21660](https://github.com/Koenkk/zigbee2mqtt/issues/21660)) ([aaed8ab](https://github.com/Koenkk/zigbee2mqtt/commit/aaed8ab95ec031ad5ea1f00933cc5f79b58fce29))
## [1.36.0](https://github.com/Koenkk/zigbee2mqtt/compare/1.35.3...1.36.0) (2024-03-01)
### Features
* Add experimental Ember adapter @Nerivec https://github.com/Koenkk/zigbee-herdsman/pull/918 ([#21459](https://github.com/Koenkk/zigbee2mqtt/issues/21459)) ([c5c0a8b](https://github.com/Koenkk/zigbee2mqtt/commit/c5c0a8b1938ed51887b21ded06d584f200ba32d1))
* Add support for button entity in Home Assistant ([#21372](https://github.com/Koenkk/zigbee2mqtt/issues/21372)) ([24b5f9b](https://github.com/Koenkk/zigbee2mqtt/commit/24b5f9b43af8303f87fd5f29e57cca4a850d7b1d))
### Bug Fixes
* `trigger_count` & `power_outage_count` adjustments in HA ([#21492](https://github.com/Koenkk/zigbee2mqtt/issues/21492)) ([fe73db4](https://github.com/Koenkk/zigbee2mqtt/commit/fe73db4ff686915841fc045750c7c7c98457422d))
* Add noise and noise_detected to HA lookup ([#21610](https://github.com/Koenkk/zigbee2mqtt/issues/21610)) ([9b8583e](https://github.com/Koenkk/zigbee2mqtt/commit/9b8583e778ffbd92ba82315d5a53ed68f51e4fc3))
* Check if Zigbee2MQTT is running in `update.sh` ([#21494](https://github.com/Koenkk/zigbee2mqtt/issues/21494)) ([8b69443](https://github.com/Koenkk/zigbee2mqtt/commit/8b694432aaa229823674eb4933eded3f2ab1517a))
* Fix endpoint matching for TuYa dummy endpoints ([#21576](https://github.com/Koenkk/zigbee2mqtt/issues/21576)) ([956003a](https://github.com/Koenkk/zigbee2mqtt/commit/956003aa0ab104f611cf3d0c61bec1c476ac17d0))
* Fix parsing entity name that contains slash in the name and endpoint specification ([#21493](https://github.com/Koenkk/zigbee2mqtt/issues/21493)) ([3c60965](https://github.com/Koenkk/zigbee2mqtt/commit/3c609658fd788410270af3343f01cc47b6ef0ab1))
* **ignore:** update dependencies ([#21347](https://github.com/Koenkk/zigbee2mqtt/issues/21347)) ([ce41f2f](https://github.com/Koenkk/zigbee2mqtt/commit/ce41f2f8ba89d31926cdce7d2d93ec19ffccf7fc))
* **ignore:** update dependencies ([#21444](https://github.com/Koenkk/zigbee2mqtt/issues/21444)) ([16b251d](https://github.com/Koenkk/zigbee2mqtt/commit/16b251d3c9c0651436899de6b63033d98e7b1353))
* **ignore:** update dependencies ([#21561](https://github.com/Koenkk/zigbee2mqtt/issues/21561)) ([c56470c](https://github.com/Koenkk/zigbee2mqtt/commit/c56470c83fd3b9ea366f67814e09146bcac6d188))
* **ignore:** update zigbee-herdsman to 0.33.9 ([#21386](https://github.com/Koenkk/zigbee2mqtt/issues/21386)) ([dfa4996](https://github.com/Koenkk/zigbee2mqtt/commit/dfa499659ec69db40ccc3d5c1dff874f446587d0))
* **ignore:** update zigbee-herdsman to 0.34.1 ([#21461](https://github.com/Koenkk/zigbee2mqtt/issues/21461)) ([b51eef2](https://github.com/Koenkk/zigbee2mqtt/commit/b51eef2319adf2b26b7b9a4a279adf2b671dd380))
* **ignore:** update zigbee-herdsman to 0.34.2 ([#21478](https://github.com/Koenkk/zigbee2mqtt/issues/21478)) ([78b5e6f](https://github.com/Koenkk/zigbee2mqtt/commit/78b5e6fd4b11ba138f83e848c60ac017fe5b10f2))
* **ignore:** update zigbee-herdsman to 0.34.3 ([#21530](https://github.com/Koenkk/zigbee2mqtt/issues/21530)) ([e726d59](https://github.com/Koenkk/zigbee2mqtt/commit/e726d5907e86ca3fabef4491579994d9e89f0fe0))
* **ignore:** update zigbee-herdsman to 0.34.4 ([#21545](https://github.com/Koenkk/zigbee2mqtt/issues/21545)) ([23959c5](https://github.com/Koenkk/zigbee2mqtt/commit/23959c5b0ec7e36183400920617e3052cd9bf17a))
* **ignore:** update zigbee-herdsman to 0.34.5 ([#21563](https://github.com/Koenkk/zigbee2mqtt/issues/21563)) ([cc27032](https://github.com/Koenkk/zigbee2mqtt/commit/cc2703275272c268b1f04ce75488027d6c70b870))
* **ignore:** update zigbee-herdsman to 0.34.6 ([#21567](https://github.com/Koenkk/zigbee2mqtt/issues/21567)) ([4f71c1e](https://github.com/Koenkk/zigbee2mqtt/commit/4f71c1e8bf3fe430cc03d3054a6e2a75ba6ec7de))
* **ignore:** update zigbee-herdsman to 0.34.7 ([#21575](https://github.com/Koenkk/zigbee2mqtt/issues/21575)) ([a06a0ea](https://github.com/Koenkk/zigbee2mqtt/commit/a06a0ea059354ac43de47c9d4e6d573bd6186288))
* **ignore:** update zigbee-herdsman to 0.34.8 ([#21588](https://github.com/Koenkk/zigbee2mqtt/issues/21588)) ([fee0811](https://github.com/Koenkk/zigbee2mqtt/commit/fee0811893a6bc90b86978e5c80cdb4e1a23ef70))
* **ignore:** update zigbee-herdsman to 0.34.9 ([#21602](https://github.com/Koenkk/zigbee2mqtt/issues/21602)) ([222b67b](https://github.com/Koenkk/zigbee2mqtt/commit/222b67b1b0c9151222c4d9cc2e531bf74a080ba6))
* **ignore:** update zigbee-herdsman to 0.35.0 ([#21621](https://github.com/Koenkk/zigbee2mqtt/issues/21621)) ([a84083b](https://github.com/Koenkk/zigbee2mqtt/commit/a84083be404a54303eb9337cc86c3938a1d5d41e))
* **ignore:** update zigbee-herdsman to 0.35.1 ([#21636](https://github.com/Koenkk/zigbee2mqtt/issues/21636)) ([0d8f578](https://github.com/Koenkk/zigbee2mqtt/commit/0d8f5780ddf05d176b199a152b3a1a212bca6810))
* **ignore:** update zigbee-herdsman-converters to 18.29.1 ([#21290](https://github.com/Koenkk/zigbee2mqtt/issues/21290)) ([55653c9](https://github.com/Koenkk/zigbee2mqtt/commit/55653c974ef72e8b5d8107d1b2e1b67efb70e4e1))
* **ignore:** update zigbee-herdsman-converters to 18.30.0 ([#21315](https://github.com/Koenkk/zigbee2mqtt/issues/21315)) ([910f3a6](https://github.com/Koenkk/zigbee2mqtt/commit/910f3a68603bf4b7f31d7a80370816d02f7126b8))
* **ignore:** update zigbee-herdsman-converters to 18.31.0 ([#21359](https://github.com/Koenkk/zigbee2mqtt/issues/21359)) ([c7b820c](https://github.com/Koenkk/zigbee2mqtt/commit/c7b820c3763e6052cc6755e0e12926c316de7f42))
* **ignore:** update zigbee-herdsman-converters to 18.32.0 ([#21373](https://github.com/Koenkk/zigbee2mqtt/issues/21373)) ([0ab7390](https://github.com/Koenkk/zigbee2mqtt/commit/0ab7390a642a723fc4e1847bbd95ea12487388ab))
* **ignore:** update zigbee-herdsman-converters to 18.33.0 ([#21387](https://github.com/Koenkk/zigbee2mqtt/issues/21387)) ([2aaf878](https://github.com/Koenkk/zigbee2mqtt/commit/2aaf878889b4211b8a470c847721c75b5a91e39e))
* **ignore:** update zigbee-herdsman-converters to 18.33.1 ([#21400](https://github.com/Koenkk/zigbee2mqtt/issues/21400)) ([8a9477f](https://github.com/Koenkk/zigbee2mqtt/commit/8a9477f2bb6afbfeacf64547c8bfe4ae4f18ca7f))
* **ignore:** update zigbee-herdsman-converters to 18.33.2 ([#21416](https://github.com/Koenkk/zigbee2mqtt/issues/21416)) ([5766bf4](https://github.com/Koenkk/zigbee2mqtt/commit/5766bf479ba548d819f87de2046f554b6de15945))
* **ignore:** update zigbee-herdsman-converters to 18.34.0 ([#21456](https://github.com/Koenkk/zigbee2mqtt/issues/21456)) ([ebed9d3](https://github.com/Koenkk/zigbee2mqtt/commit/ebed9d31d710ac6853e3eee6b677cfe6e84fd556))
* **ignore:** update zigbee-herdsman-converters to 18.35.0 ([#21487](https://github.com/Koenkk/zigbee2mqtt/issues/21487)) ([b32bd30](https://github.com/Koenkk/zigbee2mqtt/commit/b32bd309ca7748d4dcb0b3ca5b3428578a86487c))
* **ignore:** update zigbee-herdsman-converters to 18.36.0 ([#21504](https://github.com/Koenkk/zigbee2mqtt/issues/21504)) ([22d3336](https://github.com/Koenkk/zigbee2mqtt/commit/22d33360f6966625afb9f456d5ffe96c42baaa3f))
* **ignore:** update zigbee-herdsman-converters to 18.37.0 ([#21533](https://github.com/Koenkk/zigbee2mqtt/issues/21533)) ([1337f54](https://github.com/Koenkk/zigbee2mqtt/commit/1337f54e4a49ac9e28b9c3eb780829915b59cb57))
* **ignore:** update zigbee-herdsman-converters to 18.37.1 ([#21549](https://github.com/Koenkk/zigbee2mqtt/issues/21549)) ([7c97902](https://github.com/Koenkk/zigbee2mqtt/commit/7c97902d4a088d680010511148b67ac19d05b91a))
* **ignore:** update zigbee-herdsman-converters to 18.40.0 ([#21603](https://github.com/Koenkk/zigbee2mqtt/issues/21603)) ([aeaa040](https://github.com/Koenkk/zigbee2mqtt/commit/aeaa0409203e19d54511601cf7e0124db6a09778))
* **ignore:** update zigbee-herdsman-converters to 18.41.0 ([#21622](https://github.com/Koenkk/zigbee2mqtt/issues/21622)) ([72333f0](https://github.com/Koenkk/zigbee2mqtt/commit/72333f0888b5be3abacb286ed0a227438d593563))
* **ignore:** update zigbee-herdsman-converters to 18.42.0 ([#21637](https://github.com/Koenkk/zigbee2mqtt/issues/21637)) ([ccc3167](https://github.com/Koenkk/zigbee2mqtt/commit/ccc31670ec5b93bddaa8cef3f965fd8cc77e51a4))
* **ignore:** update zigbee2mqtt-frontend to 0.6.157 ([#21269](https://github.com/Koenkk/zigbee2mqtt/issues/21269)) ([a479bf9](https://github.com/Koenkk/zigbee2mqtt/commit/a479bf9039248d0b0ebc7e0f65dfc8ff342090d4))
* **ignore:** update zigbee2mqtt-frontend to 0.6.158 ([#21356](https://github.com/Koenkk/zigbee2mqtt/issues/21356)) ([8bc3c46](https://github.com/Koenkk/zigbee2mqtt/commit/8bc3c46c34e1c13c893b5d52143f6e357ff58546))
* **ignore:** update zigbee2mqtt-frontend to 0.6.159 ([#21623](https://github.com/Koenkk/zigbee2mqtt/issues/21623)) ([a8a2626](https://github.com/Koenkk/zigbee2mqtt/commit/a8a262640ff075e92bfb8d0491f4f9efe8f33ce2))
* Improve nested group and device name match ([#21521](https://github.com/Koenkk/zigbee2mqtt/issues/21521)) ([471902e](https://github.com/Koenkk/zigbee2mqtt/commit/471902e53134dbf2baddb9c37e2c20105b8f0043))
* Match endpoint name in the MQTT topic based on endpoints listed in the definition ([#21214](https://github.com/Koenkk/zigbee2mqtt/issues/21214)) ([a5a87a7](https://github.com/Koenkk/zigbee2mqtt/commit/a5a87a79a80aaa261a34a8fb541f47347c4ca729))
* Only configure routers on startup https://github.com/Koenkk/zigbee2mqtt/issues/21446 ([db1e3b7](https://github.com/Koenkk/zigbee2mqtt/commit/db1e3b73d1d4a2a98879ab10e5b18f84b30bd3bc))
* Remove dependency on predefined list of endpoints (`parseEntityID()` function) ([#21264](https://github.com/Koenkk/zigbee2mqtt/issues/21264)) ([81335e5](https://github.com/Koenkk/zigbee2mqtt/commit/81335e5b4b0ed0e1dacdd764de1711f5a1481f7f))
* Remove predefined endpoint list, remove limits on device friendly name ([#21325](https://github.com/Koenkk/zigbee2mqtt/issues/21325)) ([b4473d6](https://github.com/Koenkk/zigbee2mqtt/commit/b4473d6e24de8dc2db0ae7cb7bbb014788854ae4))
## [1.35.3](https://github.com/Koenkk/zigbee2mqtt/compare/1.35.2...1.35.3) (2024-02-06)
### Bug Fixes
* **ignore:** set zhc logger ([f83709d](https://github.com/Koenkk/zigbee2mqtt/commit/f83709d96c4a98b2c5f34a2e06fe2ea26f9cb9d5))
* **ignore:** update dependencies ([#21204](https://github.com/Koenkk/zigbee2mqtt/issues/21204)) ([1df54f0](https://github.com/Koenkk/zigbee2mqtt/commit/1df54f071f301240f4a7efeb8db87a9a66babf44))
* **ignore:** update zigbee-herdsman to 0.33.7 ([#21178](https://github.com/Koenkk/zigbee2mqtt/issues/21178)) ([3d1336d](https://github.com/Koenkk/zigbee2mqtt/commit/3d1336dc6cb3f73810f4ed467df443e46cca23e0))
* **ignore:** update zigbee-herdsman-converters to 18.26.0 ([#21145](https://github.com/Koenkk/zigbee2mqtt/issues/21145)) ([08c03e4](https://github.com/Koenkk/zigbee2mqtt/commit/08c03e48cc47b344c3f5664147afd3c4fe8c1802))
* **ignore:** update zigbee-herdsman-converters to 18.27.0 ([#21184](https://github.com/Koenkk/zigbee2mqtt/issues/21184)) ([a678a1c](https://github.com/Koenkk/zigbee2mqtt/commit/a678a1c652a1a77fe80245d5944945ab7c75352b))
* **ignore:** update zigbee-herdsman-converters to 18.27.1 ([#21189](https://github.com/Koenkk/zigbee2mqtt/issues/21189)) ([4b17217](https://github.com/Koenkk/zigbee2mqtt/commit/4b1721721b1580115ff952a2f49cd0c4e0dabafb))
* **ignore:** update zigbee-herdsman-converters to 18.28.0 ([#21207](https://github.com/Koenkk/zigbee2mqtt/issues/21207)) ([3d37892](https://github.com/Koenkk/zigbee2mqtt/commit/3d3789229692eb522ef3f4b5d920f90336cf2be9))
* **ignore:** update zigbee-herdsman-converters to 18.28.1 ([#21222](https://github.com/Koenkk/zigbee2mqtt/issues/21222)) ([a6d51a6](https://github.com/Koenkk/zigbee2mqtt/commit/a6d51a60a8b338f1245a17d228212364a6bf235c))
* **ignore:** update zigbee-herdsman-converters to 18.28.2 ([#21245](https://github.com/Koenkk/zigbee2mqtt/issues/21245)) ([250d5bf](https://github.com/Koenkk/zigbee2mqtt/commit/250d5bfc24c146e6055db7f069770af02fccca7c))
* **ignore:** update zigbee-herdsman-converters to 18.28.3 ([#21265](https://github.com/Koenkk/zigbee2mqtt/issues/21265)) ([29bae3a](https://github.com/Koenkk/zigbee2mqtt/commit/29bae3a73421c18c8437d21d727d70f10e08a1e9))
* **ignore:** update zigbee2mqtt-frontend to 0.6.153 ([#21187](https://github.com/Koenkk/zigbee2mqtt/issues/21187)) ([7295955](https://github.com/Koenkk/zigbee2mqtt/commit/7295955397dd829a15de08081c08ed236dee2c21))
* **ignore:** update zigbee2mqtt-frontend to 0.6.154 ([#21188](https://github.com/Koenkk/zigbee2mqtt/issues/21188)) ([2db0723](https://github.com/Koenkk/zigbee2mqtt/commit/2db07236dbc3b8ed2817209c72fd7103efb5fba7))
* **ignore:** update zigbee2mqtt-frontend to 0.6.155 ([#21228](https://github.com/Koenkk/zigbee2mqtt/issues/21228)) ([8ab6c16](https://github.com/Koenkk/zigbee2mqtt/commit/8ab6c16f6ecbf8dde9a208ea51f48603f5e21b68))
* **ignore:** update zigbee2mqtt-frontend to 0.6.156 ([#21229](https://github.com/Koenkk/zigbee2mqtt/issues/21229)) ([dfda631](https://github.com/Koenkk/zigbee2mqtt/commit/dfda63138ff4e0c6a0b28503d3e9c9482991b8b8))
## [1.35.2](https://github.com/Koenkk/zigbee2mqtt/compare/1.35.1...1.35.2) (2024-01-31)
Executable → Regular
+120 -1
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 25 KiB

+8 -5
View File
@@ -8,6 +8,8 @@ import utils from './util/utils';
import stringify from 'json-stable-stringify-without-jsonify';
import assert from 'assert';
import bind from 'bind-decorator';
import {setLogger as zhcSetLogger} from 'zigbee-herdsman-converters';
import {setLogger as zhSetLogger} from 'zigbee-herdsman';
// Extensions
import ExtensionFrontend from './extension/frontend';
@@ -59,6 +61,8 @@ export class Controller {
constructor(restartCallback: () => void, exitCallback: (code: number, restart: boolean) => void) {
logger.init();
zhSetLogger(logger);
zhcSetLogger(logger);
this.eventBus = new EventBus();
this.zigbee = new Zigbee(this.eventBus);
this.mqtt = new MQTT(this.eventBus);
@@ -71,6 +75,7 @@ export class Controller {
this.enableDisableExtension, this.restartCallback, this.addExtension];
this.extensions = [
new ExtensionOnEvent(...this.extensionArgs),
new ExtensionBridge(...this.extensionArgs),
new ExtensionPublish(...this.extensionArgs),
new ExtensionReceive(...this.extensionArgs),
@@ -79,7 +84,6 @@ export class Controller {
new ExtensionNetworkMap(...this.extensionArgs),
new ExtensionGroups(...this.extensionArgs),
new ExtensionBind(...this.extensionArgs),
new ExtensionOnEvent(...this.extensionArgs),
new ExtensionOTAUpdate(...this.extensionArgs),
new ExtensionReport(...this.extensionArgs),
new ExtensionExternalExtension(...this.extensionArgs),
@@ -95,7 +99,6 @@ export class Controller {
async start(): Promise<void> {
this.state.start();
logger.logOutput();
const info = await utils.getZigbee2MQTTVersion();
logger.info(`Starting Zigbee2MQTT version ${info.version} (commit #${info.commitHash})`);
@@ -135,9 +138,9 @@ export class Controller {
// Enable zigbee join
try {
if (settings.get().permit_join) {
logger.warn('`permit_join` set to `true` in configuration.yaml.');
logger.warn('Allowing new devices to join.');
logger.warn('Set `permit_join` to `false` once you joined all devices.');
logger.warning('`permit_join` set to `true` in configuration.yaml.');
logger.warning('Allowing new devices to join.');
logger.warning('Set `permit_join` to `false` once you joined all devices.');
}
await this.zigbee.permitJoin(settings.get().permit_join);
+2 -2
View File
@@ -87,7 +87,7 @@ export default class Availability extends Extension {
logger.debug(`Successfully pinged '${device.name}' (attempt ${i + 1}/${attempts})`);
break;
} catch (error) {
logger.warn(`Failed to ping '${device.name}' (attempt ${i + 1}/${attempts}, ${error.message})`);
logger.warning(`Failed to ping '${device.name}' (attempt ${i + 1}/${attempts}, ${error.message})`);
// Try again in 3 seconds.
const lastAttempt = i - 1 === attempts;
!lastAttempt && await utils.sleep(3);
@@ -215,7 +215,7 @@ export default class Availability extends Extension {
const options: KeyValue = device.options;
const state = this.state.get(device);
const meta: zhc.Tz.Meta = {
message: this.state.get(device), mapped: device.definition, logger, endpoint_name: null,
message: this.state.get(device), mapped: device.definition, endpoint_name: null,
options, state, device: device.zh,
};
await converter?.convertGet?.(device.endpoint(), item.keys[0], meta)
+30 -27
View File
@@ -4,7 +4,7 @@ import utils from '../util/utils';
import Extension from './extension';
import stringify from 'json-stable-stringify-without-jsonify';
import debounce from 'debounce';
import * as zigbeeHerdsman from 'zigbee-herdsman/dist';
import {Zcl} from 'zigbee-herdsman';
import bind from 'bind-decorator';
import Device from '../model/device';
import Group from '../model/group';
@@ -97,12 +97,12 @@ const pollOnMessage: PollOnMessage = [
read: {cluster: 'genLevelCtrl', attributes: ['currentLevel']},
// When the bound devices/members of group have the following manufacturerIDs
manufacturerIDs: [
zigbeeHerdsman.Zcl.ManufacturerCode.Philips,
zigbeeHerdsman.Zcl.ManufacturerCode.ATMEL,
zigbeeHerdsman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
zigbeeHerdsman.Zcl.ManufacturerCode.MUELLER_LICHT_INT,
zigbeeHerdsman.Zcl.ManufacturerCode.TELINK,
zigbeeHerdsman.Zcl.ManufacturerCode.BUSCH_JAEGER,
Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V,
Zcl.ManufacturerCode.ATMEL,
Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
Zcl.ManufacturerCode.MUELLER_LICHT_INTERNATIONAL_INC,
Zcl.ManufacturerCode.TELINK_MICRO,
Zcl.ManufacturerCode.BUSCH_JAEGER_ELEKTRO,
],
manufacturerNames: [
'GLEDOPTO',
@@ -133,12 +133,12 @@ const pollOnMessage: PollOnMessage = [
},
read: {cluster: 'genOnOff', attributes: ['onOff']},
manufacturerIDs: [
zigbeeHerdsman.Zcl.ManufacturerCode.Philips,
zigbeeHerdsman.Zcl.ManufacturerCode.ATMEL,
zigbeeHerdsman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
zigbeeHerdsman.Zcl.ManufacturerCode.MUELLER_LICHT_INT,
zigbeeHerdsman.Zcl.ManufacturerCode.TELINK,
zigbeeHerdsman.Zcl.ManufacturerCode.BUSCH_JAEGER,
Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V,
Zcl.ManufacturerCode.ATMEL,
Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
Zcl.ManufacturerCode.MUELLER_LICHT_INTERNATIONAL_INC,
Zcl.ManufacturerCode.TELINK_MICRO,
Zcl.ManufacturerCode.BUSCH_JAEGER_ELEKTRO,
],
manufacturerNames: [
'GLEDOPTO',
@@ -165,11 +165,11 @@ const pollOnMessage: PollOnMessage = [
},
},
manufacturerIDs: [
zigbeeHerdsman.Zcl.ManufacturerCode.Philips,
zigbeeHerdsman.Zcl.ManufacturerCode.ATMEL,
zigbeeHerdsman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
zigbeeHerdsman.Zcl.ManufacturerCode.MUELLER_LICHT_INT,
zigbeeHerdsman.Zcl.ManufacturerCode.TELINK,
Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V,
Zcl.ManufacturerCode.ATMEL,
Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
Zcl.ManufacturerCode.MUELLER_LICHT_INTERNATIONAL_INC,
Zcl.ManufacturerCode.TELINK_MICRO,
// Note: ManufacturerCode.BUSCH_JAEGER is left out intentionally here as their devices don't support colors
],
manufacturerNames: [
@@ -222,25 +222,28 @@ export default class Bind extends Extension {
const message = utils.parseJSON(data.message, data.message);
let error = null;
const parsedSource = utils.parseEntityID(sourceKey);
const parsedTarget = utils.parseEntityID(targetKey);
const source = this.zigbee.resolveEntity(parsedSource.ID);
const target = targetKey === 'default_bind_group' ?
defaultBindGroup : this.zigbee.resolveEntity(parsedTarget.ID);
const parsedSource = this.zigbee.resolveEntityAndEndpoint(sourceKey);
const parsedTarget = this.zigbee.resolveEntityAndEndpoint(targetKey);
const source = parsedSource.entity;
const target = targetKey === 'default_bind_group' ? defaultBindGroup : parsedTarget.entity;
const responseData: KeyValue = {from: sourceKey, to: targetKey};
if (!source || !(source instanceof Device)) {
error = `Source device '${sourceKey}' does not exist`;
} else if (parsedSource.endpointID && !parsedSource.endpoint) {
error = `Source device '${parsedSource.ID}' does not have endpoint '${parsedSource.endpointID}'`;
} else if (!target) {
error = `Target device or group '${targetKey}' does not exist`;
} else if (target instanceof Device && parsedTarget.endpointID && !parsedTarget.endpoint) {
error = `Target device '${parsedTarget.ID}' does not have endpoint '${parsedTarget.endpointID}'`;
} else {
const successfulClusters: string[] = [];
const failedClusters = [];
const attemptedClusters = [];
const bindSource: zh.Endpoint = source.endpoint(parsedSource.endpoint);
const bindSource: zh.Endpoint = parsedSource.endpoint;
let bindTarget: number | zh.Group | zh.Endpoint = null;
if (target instanceof Device) bindTarget = target.endpoint(parsedTarget.endpoint);
if (target instanceof Device) bindTarget = parsedTarget.endpoint;
else if (target instanceof Group) bindTarget = target.zh;
else bindTarget = Number(target.ID);
@@ -391,7 +394,7 @@ export default class Bind extends Extension {
await endpoint.configureReporting(bind.cluster.name, items);
logger.info(`Successfully setup reporting for '${entity}' cluster '${bind.cluster.name}'`);
} catch (error) {
logger.warn(`Failed to setup reporting for '${entity}' cluster '${bind.cluster.name}'`);
logger.warning(`Failed to setup reporting for '${entity}' cluster '${bind.cluster.name}'`);
}
}
}
@@ -433,7 +436,7 @@ export default class Bind extends Extension {
await endpoint.configureReporting(cluster, items);
logger.info(`Successfully disabled reporting for '${entity}' cluster '${cluster}'`);
} catch (error) {
logger.warn(`Failed to disable reporting for '${entity}' cluster '${cluster}'`);
logger.warning(`Failed to disable reporting for '${entity}' cluster '${cluster}'`);
}
}
+123 -29
View File
@@ -13,6 +13,9 @@ import data from '../util/data';
import JSZip from 'jszip';
import fs from 'fs';
import * as zhc from 'zigbee-herdsman-converters';
import {CustomClusters, ClusterDefinition, ClusterName} from 'zigbee-herdsman/dist/zspec/zcl/definition/tstype';
import {Clusters} from 'zigbee-herdsman/dist/zspec/zcl/definition/cluster';
import winston from 'winston';
const requestRegex = new RegExp(`${settings.get().mqtt.base_topic}/bridge/request/(.*)`);
@@ -28,6 +31,8 @@ export default class Bridge extends Extension {
private coordinatorVersion: zh.CoordinatorVersion;
private restartRequired = false;
private lastJoinedDeviceIeeeAddr: string;
private lastBridgeLoggingPayload: string;
private logTransport: winston.transport;
private requestLookup: {[key: string]: (message: KeyValue | string) => Promise<MQTTResponse>};
override async start(): Promise<void> {
@@ -35,6 +40,7 @@ export default class Bridge extends Extension {
'device/options': this.deviceOptions,
'device/configure_reporting': this.deviceConfigureReporting,
'device/remove': this.deviceRemove,
'device/interview': this.deviceInterview,
'device/generate_external_definition': this.deviceGenerateExternalDefinition,
'device/rename': this.deviceRename,
'group/add': this.groupAdd,
@@ -58,16 +64,41 @@ export default class Bridge extends Extension {
'config/log_level': this.configLogLevel,
};
const mqtt = this.mqtt;
class EventTransport extends Transport {
log(info: {message: string, level: string}, callback: () => void): void {
const payload = stringify({message: info.message, level: info.level});
mqtt.publish(`bridge/logging`, payload, {}, settings.get().mqtt.base_topic, true);
callback();
const debugToMQTTFrontend = settings.get().advanced.log_debug_to_mqtt_frontend;
const baseTopic = settings.get().mqtt.base_topic;
const bridgeLogging = (message: string, level: string, namespace: string): void => {
const payload = stringify({message, level, namespace});
if (payload !== this.lastBridgeLoggingPayload) {
this.lastBridgeLoggingPayload = payload;
this.mqtt.publish(`bridge/logging`, payload, {}, baseTopic, true);
}
};
if (debugToMQTTFrontend) {
class DebugEventTransport extends Transport {
log(info: {message: string, level: string, namespace: string}, next: () => void): void {
bridgeLogging(info.message, info.level, info.namespace);
next();
}
}
this.logTransport = new DebugEventTransport();
} else {
class EventTransport extends Transport {
log(info: {message: string, level: string, namespace: string}, next: () => void): void {
if (info.level !== 'debug') {
bridgeLogging(info.message, info.level, info.namespace);
}
next();
}
}
this.logTransport = new EventTransport();
}
logger.addTransport(new EventTransport());
logger.addTransport(this.logTransport);
this.zigbee2mqttVersion = await utils.getZigbee2MQTTVersion();
this.zigbeeHerdsmanVersion = await utils.getDependencyVersion('zigbee-herdsman');
@@ -76,7 +107,9 @@ export default class Bridge extends Extension {
this.eventBus.onEntityRenamed(this, () => this.publishInfo());
this.eventBus.onGroupMembersChanged(this, () => this.publishGroups());
this.eventBus.onDevicesChanged(this, () => this.publishDevices() && this.publishInfo());
this.eventBus.onDevicesChanged(this, () => this.publishDevices() &&
this.publishInfo() &&
this.publishDefinitions());
this.eventBus.onPermitJoinChanged(this, () => !this.zigbee.isStopping() && this.publishInfo());
this.eventBus.onScenesChanged(this, () => {
this.publishDevices();
@@ -93,6 +126,7 @@ export default class Bridge extends Extension {
});
this.eventBus.onDeviceLeave(this, (data) => {
this.publishDevices();
this.publishDefinitions();
publishEvent('device_leave', {ieee_address: data.ieeeAddr, friendly_name: data.name});
});
this.eventBus.onDeviceNetworkAddressChanged(this, () => this.publishDevices());
@@ -114,10 +148,16 @@ export default class Bridge extends Extension {
await this.publishInfo();
await this.publishDevices();
await this.publishGroups();
await this.publishDefinitions();
this.eventBus.onMQTTMessage(this, this.onMQTTMessage);
}
override async stop(): Promise<void> {
super.stop();
logger.removeTransport(this.logTransport);
}
@bind async onMQTTMessage(data: eventdata.MQTTMessage): Promise<void> {
const match = data.topic.match(requestRegex);
const key = match?.[1]?.toLowerCase();
@@ -153,21 +193,29 @@ export default class Bridge extends Extension {
throw new Error(`Invalid payload`);
}
const newSettings = utils.computeSettingsToChange(settings.get(), message.options);
const newSettings = message.options;
const restartRequired = settings.apply(newSettings);
if (restartRequired) this.restartRequired = true;
// Apply some settings on-the-fly.
if (newSettings.hasOwnProperty('permit_join')) {
await this.zigbee.permitJoin(newSettings.permit_join);
if (newSettings.permit_join != undefined) {
await this.zigbee.permitJoin(settings.get().permit_join);
}
if (newSettings.hasOwnProperty('homeassistant')) {
await this.enableDisableExtension(newSettings.homeassistant, 'HomeAssistant');
if (newSettings.homeassistant != undefined) {
await this.enableDisableExtension(!!settings.get().homeassistant, 'HomeAssistant');
}
if (newSettings.hasOwnProperty('advanced') && newSettings.advanced.hasOwnProperty('log_level')) {
logger.setLevel(newSettings.advanced.log_level);
if (newSettings.advanced?.log_level != undefined) {
logger.setLevel(settings.get().advanced.log_level);
}
if (newSettings.advanced?.log_namespaced_levels != undefined) {
logger.setNamespacedLevels(settings.get().advanced.log_namespaced_levels);
}
if (newSettings.advanced?.log_debug_namespace_ignore != undefined) {
logger.setDebugNamespaceIgnore(settings.get().advanced.log_debug_namespace_ignore);
}
logger.info('Successfully changed options');
@@ -321,10 +369,9 @@ export default class Bridge extends Extension {
// Deprecated
@bind async configLogLevel(message: KeyValue | string): Promise<MQTTResponse> {
const allowed = ['error', 'warn', 'info', 'debug'];
const value = this.getValue(message) as 'error' | 'warn' | 'info' | 'debug';
if (typeof value !== 'string' || !allowed.includes(value)) {
throw new Error(`'${value}' is not an allowed value, allowed: ${allowed}`);
const value = this.getValue(message) as settings.LogLevel;
if (typeof value !== 'string' || !settings.LOG_LEVELS.includes(value)) {
throw new Error(`'${value}' is not an allowed value, allowed: ${settings.LOG_LEVELS}`);
}
logger.setLevel(value);
@@ -404,11 +451,8 @@ export default class Bridge extends Extension {
const ID = message.id;
const entity = this.getEntity(entityType, ID);
const currentOptions = entityType === 'device' ? settings.get().devices[entity.ID] :
settings.get().groups[entity.ID];
const options = utils.computeSettingsToChange(currentOptions, message.options);
const oldOptions = objectAssignDeep({}, cleanup(entity.options));
const restartRequired = settings.changeEntityOptions(ID, options);
const restartRequired = settings.changeEntityOptions(ID, message.options);
if (restartRequired) this.restartRequired = true;
const newOptions = cleanup(entity.options);
await this.publishInfo();
@@ -430,8 +474,13 @@ export default class Bridge extends Extension {
throw new Error(`Invalid payload`);
}
const parsedID = utils.parseEntityID(message.id);
const endpoint = (this.getEntity('device', parsedID.ID) as Device).endpoint(parsedID.endpoint);
const device = this.zigbee.resolveEntityAndEndpoint(message.id);
if (!device.entity) throw new Error(`Device '${message.id}' does not exist`);
const endpoint = device.endpoint;
if (device.endpointID && !endpoint) {
throw new Error(`Device '${device.ID}' does not have endpoint '${device.endpointID}'`);
}
const coordinatorEndpoint = this.zigbee.firstCoordinatorEndpoint();
await endpoint.bind(message.cluster, coordinatorEndpoint);
@@ -452,13 +501,33 @@ export default class Bridge extends Extension {
}, null);
}
@bind async deviceInterview(message: string | KeyValue): Promise<MQTTResponse> {
if (typeof message !== 'object' || !message.hasOwnProperty('id')) {
throw new Error(`Invalid payload`);
}
const device = this.getEntity('device', message.id) as Device;
try {
await device.zh.interview();
} catch (error) {
throw new Error(`interview of '${device.name}' (${device.ieeeAddr}) failed: ${error}`, {cause: error});
}
// publish devices so that the front-end has up-to-date info.
await this.publishDevices();
return utils.getResponse(message, {id: message.id}, null);
}
@bind async deviceGenerateExternalDefinition(message: string | KeyValue): Promise<MQTTResponse> {
if (typeof message !== 'object' || !message.hasOwnProperty('id')) {
throw new Error(`Invalid payload`);
}
const parsedID = utils.parseEntityID(message.id);
const device = this.getEntity('device', parsedID.ID) as Device;
const device = this.zigbee.resolveEntityAndEndpoint(message.id).entity as Device;
if (!device) throw new Error(`Device '${message.id}' does not exist`);
const source = await zhc.generateExternalDefinitionSource(device.zh);
return utils.getResponse(message, {id: message.id, source}, null);
@@ -536,7 +605,7 @@ export default class Bridge extends Extension {
}
if (force) {
await entity.zh.removeFromDatabase();
entity.zh.removeFromDatabase();
} else {
await entity.zh.removeFromNetwork();
}
@@ -571,6 +640,8 @@ export default class Bridge extends Extension {
if (entity instanceof Device) {
this.publishGroups();
this.publishDevices();
// Refresh Cluster definition
this.publishDefinitions();
return utils.getResponse(message, {id: ID, block, force}, null);
} else {
this.publishGroups();
@@ -699,6 +770,27 @@ export default class Bridge extends Extension {
'bridge/groups', stringify(groups), {retain: true, qos: 0}, settings.get().mqtt.base_topic, true);
}
async publishDefinitions(): Promise<void> {
interface ClusterDefinitionPayload {
clusters: Readonly<Record<ClusterName, Readonly<ClusterDefinition>>>,
custom_clusters: {[key: string] : CustomClusters}
}
const data: ClusterDefinitionPayload = {
clusters: Clusters,
custom_clusters: {},
};
for (const device of this.zigbee.devices()) {
if (Object.keys(device.customClusters).length !== 0) {
data.custom_clusters[device.ieeeAddr] = device.customClusters;
}
}
await this.mqtt.publish('bridge/definitions', stringify(data),
{retain: true, qos: 0}, settings.get().mqtt.base_topic, true);
}
getDefinitionPayload(device: Device): DefinitionPayload {
if (!device.definition) return null;
// @ts-expect-error icon is valid for external definitions
@@ -709,7 +801,7 @@ export default class Bridge extends Extension {
icon = icon.replace('${model}', utils.sanitizeImageParameter(device.definition.model));
}
return {
const payload: DefinitionPayload = {
model: device.definition.model,
vendor: device.definition.vendor,
description: device.definition.description,
@@ -718,5 +810,7 @@ export default class Bridge extends Extension {
options: device.definition.options,
icon,
};
return payload;
}
}
+6 -5
View File
@@ -35,7 +35,7 @@ export default class Configure extends Extension {
}
if (!device.definition || !device.definition.configure) {
logger.warn(`Skipping configure of '${device.name}', device does not require this.`);
logger.warning(`Skipping configure of '${device.name}', device does not require this.`);
return;
}
@@ -65,7 +65,9 @@ export default class Configure extends Extension {
override async start(): Promise<void> {
setImmediate(async () => {
for (const device of this.zigbee.devices(false)) {
// Only configure routers on startup, end devices are likely sleeping and
// will reconfigure once they send a message
for (const device of this.zigbee.devices(false).filter((d) => d.zh.type === 'Router')) {
// Sleep 10 seconds between configuring on startup to not DDoS the coordinator
// when many devices have to be configured.
await utils.sleep(10);
@@ -94,8 +96,7 @@ export default class Configure extends Extension {
return;
}
if (device.zh.meta?.hasOwnProperty('configured') &&
device.zh.meta.configured === zhc.getConfigureKey(device.definition)) {
if (device.zh.meta?.hasOwnProperty('configured')) {
return;
}
@@ -117,7 +118,7 @@ export default class Configure extends Extension {
logger.info(`Configuring '${device.name}'`);
try {
await device.definition.configure(device.zh, this.zigbee.firstCoordinatorEndpoint(), logger);
await device.definition.configure(device.zh, this.zigbee.firstCoordinatorEndpoint(), device.definition);
logger.info(`Successfully configured '${device.name}'`);
device.zh.meta.configured = zhc.getConfigureKey(device.definition);
device.zh.save();
+1
View File
@@ -152,6 +152,7 @@ export default class Frontend extends Extension {
}
@bind private onMQTTPublishMessage(data: eventdata.MQTTMessagePublished): void {
/* istanbul ignore else */
if (data.topic.startsWith(`${this.mqttBaseTopic}/`)) {
// Send topic without base_topic
const topic = data.topic.substring(this.mqttBaseTopic.length + 1);
+22 -10
View File
@@ -68,10 +68,13 @@ export default class Groups extends Extension {
const groupID = settingGroup.ID;
const zigbeeGroup = zigbeeGroups.find((g) => g.ID === groupID) || this.zigbee.createGroup(groupID);
const settingsEndpoint = settingGroup.devices.map((d) => {
const parsed = utils.parseEntityID(d);
const entity = this.zigbee.resolveEntity(parsed.ID) as Device;
const parsed = this.zigbee.resolveEntityAndEndpoint(d);
const entity = parsed.entity as Device;
if (!entity) logger.error(`Cannot find '${d}' of group '${settingGroup.friendly_name}'`);
return {'endpoint': entity?.endpoint(parsed.endpoint), 'name': entity?.name};
if (parsed.endpointID && !parsed.endpoint) {
logger.error(`Cannot find endpoint '${parsed.endpointID}' of device '${parsed.ID}'`);
}
return {'endpoint': parsed.endpoint, 'name': entity?.name};
}).filter((e) => e.endpoint != null);
// In settings but not in zigbee
@@ -109,8 +112,9 @@ export default class Groups extends Extension {
const payload: KeyValue = {};
let endpointName: string = null;
const endpointNames: string[] = data.entity instanceof Device ? data.entity.getEndpointNames() : [];
for (let [prop, value] of Object.entries(data.update)) {
const endpointNameMatch = utils.endpointNames.find((n) => prop.endsWith(`_${n}`));
const endpointNameMatch = endpointNames.find((n) => prop.endsWith(`_${n}`));
if (endpointNameMatch) {
prop = prop.substring(0, prop.length - endpointNameMatch.length - 1);
endpointName = endpointNameMatch;
@@ -237,8 +241,8 @@ export default class Groups extends Extension {
type = 'remove_all';
}
const parsedEntity = utils.parseEntityID(data.message);
resolvedEntityDevice = this.zigbee.resolveEntity(parsedEntity.ID) as Device;
const parsedEntity = this.zigbee.resolveEntityAndEndpoint(data.message);
resolvedEntityDevice = parsedEntity.entity as Device;
if (!resolvedEntityDevice || !(resolvedEntityDevice instanceof Device)) {
logger.error(`Device '${data.message}' does not exist`);
@@ -255,7 +259,12 @@ export default class Groups extends Extension {
return null;
}
resolvedEntityEndpoint = resolvedEntityDevice.endpoint(parsedEntity.endpoint);
resolvedEntityEndpoint = parsedEntity.endpoint;
if (parsedEntity.endpointID && !resolvedEntityEndpoint) {
logger.error(`Device '${parsedEntity.ID}' does not have endpoint '${parsedEntity.endpointID}'`);
return null;
}
} else if (topicRegexMatch) {
type = topicRegexMatch[1] as 'remove' | 'add' | 'remove_all';
const message = JSON.parse(data.message);
@@ -270,13 +279,16 @@ export default class Groups extends Extension {
}
}
const parsed = utils.parseEntityID(message.device);
resolvedEntityDevice = this.zigbee.resolveEntity(parsed.ID) as Device;
const parsed = this.zigbee.resolveEntityAndEndpoint(message.device);
resolvedEntityDevice = parsed?.entity as Device;
if (!error && (!resolvedEntityDevice || !(resolvedEntityDevice instanceof Device))) {
error = `Device '${message.device}' does not exist`;
}
if (!error) {
resolvedEntityEndpoint = resolvedEntityDevice.endpoint(parsed.endpoint);
resolvedEntityEndpoint = parsed.endpoint;
if (parsed.endpointID && !resolvedEntityEndpoint) {
error = `Device '${parsed.ID}' does not have endpoint '${parsed.endpointID}'`;
}
}
}
+143 -91
View File
@@ -23,6 +23,13 @@ const sensorClick: DiscoveryEntry = {
},
};
interface Discovered {
mockProperties: Set<MockProperty>,
messages: {[s: string]: {payload: string, published: boolean}},
triggers: Set<string>,
discovered: boolean,
}
const ACCESS_STATE = 0b001;
const ACCESS_SET = 0b010;
const groupSupportedTypes = ['light', 'switch', 'lock', 'cover'];
@@ -106,10 +113,10 @@ class Bridge {
* This extensions handles integration with HomeAssistant
*/
export default class HomeAssistant extends Extension {
private discovered: {[s: string]:
{topics: Set<string>, mockProperties: Set<MockProperty>, objectIDs: Set<string>}} = {};
private discoveredTriggers : {[s: string]: Set<string>}= {};
private discovered: {[s: string]: Discovered} = {};
private discoveryTopic = settings.get().homeassistant.discovery_topic;
private discoveryRegex = new RegExp(`${settings.get().homeassistant.discovery_topic}/(.*)/(.*)/(.*)/config`);
private discoveryRegexWoTopic = new RegExp(`(.*)/(.*)/(.*)/config`);
private statusTopic = settings.get().homeassistant.status_topic;
private entityAttributes = settings.get().homeassistant.legacy_entity_attributes;
private zigbee2MQTTVersion: string;
@@ -128,7 +135,7 @@ export default class HomeAssistant extends Extension {
override async start(): Promise<void> {
if (!settings.get().advanced.cache_state) {
logger.warn('In order for Home Assistant integration to work properly set `cache_state: true');
logger.warning('In order for Home Assistant integration to work properly set `cache_state: true');
}
this.zigbee2MQTTVersion = (await utils.getZigbee2MQTTVersion(false)).version;
@@ -145,22 +152,41 @@ export default class HomeAssistant extends Extension {
this.eventBus.onDeviceInterview(this, this.onZigbeeEvent);
this.eventBus.onDeviceMessage(this, this.onZigbeeEvent);
this.eventBus.onScenesChanged(this, this.onScenesChanged);
this.eventBus.onEntityOptionsChanged(this, (data) => this.discover(data.entity, true));
this.eventBus.onExposesChanged(this, (data) => this.discover(data.device, true));
this.eventBus.onEntityOptionsChanged(this, (data) => this.discover(data.entity));
this.eventBus.onExposesChanged(this, (data) => this.discover(data.device));
this.mqtt.subscribe(this.statusTopic);
this.mqtt.subscribe(defaultStatusTopic);
this.mqtt.subscribe(`${this.discoveryTopic}/#`);
// MQTT discovery of all paired devices on startup.
for (const entity of [this.bridge, ...this.zigbee.devices(false), ...this.zigbee.groups()]) {
this.discover(entity, true);
}
/**
* Prevent unecessary re-discovery of entities by waiting 5 seconds for retained discovery messages to come in.
* Any received discovery messages will not be published again.
* Unsubscribe from the discoveryTopic to prevent receiving our own messages.
*/
const discoverWait = 5;
// Discover with `published = false`, this will populate `this.discovered` without publishing the discoveries.
// This is needed for clearing outdated entries in `this.onMQTTMessage()`
[this.bridge, ...this.zigbee.devices(false), ...this.zigbee.groups()].forEach((e) => this.discover(e, false));
logger.debug(`Discovering entities to Home Assistant in ${discoverWait}s`);
this.mqtt.subscribe(`${this.discoveryTopic}/#`);
setTimeout(() => {
this.mqtt.unsubscribe(`${this.discoveryTopic}/#`);
logger.debug(`Discovering entities to Home Assistant`);
[this.bridge, ...this.zigbee.devices(false), ...this.zigbee.groups()].forEach((e) => this.discover(e));
}, utils.seconds(discoverWait));
// Send availability messages, this is required if the legacy_availability_payload option has been changed.
this.eventBus.emitPublishAvailability();
}
private getDiscovered(entity: Device | Group | Bridge | string): Discovered {
const ID = typeof entity === 'string' ? entity : entity.ID;
if (!(ID in this.discovered)) {
this.discovered[ID] = {messages: {}, triggers: new Set(), mockProperties: new Set(), discovered: false};
}
return this.discovered[ID];
}
private exposeToConfig(exposes: zhc.Expose[], entityType: 'device' | 'group',
allExposes: zhc.Expose[], definition?: zhc.Definition): DiscoveryEntry[] {
// For groups an array of exposes (of the same type) is passed, this is to determine e.g. what features
@@ -210,7 +236,6 @@ export default class HomeAssistant extends Extension {
].filter((c) => c);
if (colorModes.length) {
discoveryEntry.discovery_payload.color_mode = true;
discoveryEntry.discovery_payload.supported_color_modes = colorModes;
}
@@ -600,6 +625,7 @@ export default class HomeAssistant extends Extension {
discoveryEntries.push(discoveryEntry);
} else if (isBinaryExposeFeature(firstExpose)) {
const lookup: {[s: string]: KeyValue}= {
activity_led_indicator: {icon: 'mdi:led-on'},
auto_off: {icon: 'mdi:flash-auto'},
battery_low: {entity_category: 'diagnostic', device_class: 'battery'},
button_lock: {entity_category: 'config', icon: 'mdi:lock'},
@@ -613,7 +639,7 @@ export default class HomeAssistant extends Extension {
card: {entity_category: 'config', icon: 'mdi:clipboard-check'},
child_lock: {entity_category: 'config', icon: 'mdi:account-lock'},
color_sync: {entity_category: 'config', icon: 'mdi:sync-circle'},
consumer_connected: {entity_category: 'diagnostic', device_class: 'connectivity'},
consumer_connected: {device_class: 'plug'},
contact: {device_class: 'door'},
garage_door_contact: {device_class: 'garage_door', payload_on: false, payload_off: true},
eco_mode: {entity_category: 'config', icon: 'mdi:leaf'},
@@ -629,6 +655,7 @@ export default class HomeAssistant extends Extension {
motor_reversal: {entity_category: 'config', icon: 'mdi:arrow-left-right'},
moving: {device_class: 'moving'},
no_position_support: {entity_category: 'config', icon: 'mdi:minus-circle-outline'},
noise_detected: {device_class: 'sound'},
occupancy: {device_class: 'motion'},
power_outage_memory: {entity_category: 'config', icon: 'mdi:memory'},
presence: {device_class: 'presence'},
@@ -644,6 +671,7 @@ export default class HomeAssistant extends Extension {
tamper: {device_class: 'tamper'},
temperature_scale: {entity_category: 'config', icon: 'mdi:temperature-celsius'},
test: {entity_category: 'diagnostic', icon: 'mdi:test-tube'},
th_heater: {icon: 'mdi:heat-wave'},
trigger_indicator: {icon: 'mdi:led-on'},
valve_alarm: {device_class: 'problem'},
valve_detection: {icon: 'mdi:pipe-valve'},
@@ -682,9 +710,6 @@ export default class HomeAssistant extends Extension {
},
};
// Let Home Assistant generate entity name when device_class is present
if (discoveryEntry.discovery_payload.device_class) delete discoveryEntry.discovery_payload.name;
discoveryEntries.push(discoveryEntry);
} else {
const discoveryEntry: DiscoveryEntry = {
@@ -700,9 +725,6 @@ export default class HomeAssistant extends Extension {
},
};
// Let Home Assistant generate entity name when device_class is present
if (discoveryEntry.discovery_payload.device_class) delete discoveryEntry.discovery_payload.name;
discoveryEntries.push(discoveryEntry);
}
} else if (isNumericExposeFeature(firstExpose)) {
@@ -792,6 +814,11 @@ export default class HomeAssistant extends Extension {
min_temperature: {entity_category: 'config', icon: 'mdi:thermometer-low'},
minimum_on_level: {entity_category: 'config'},
measurement_poll_interval: {entity_category: 'config', icon: 'mdi:clock-out'},
noise: {device_class: 'sound_pressure', state_class: 'measurement'},
noise_detect_level: {icon: 'mdi:volume-equal'},
noise_timeout: {icon: 'mdi:timer'},
occupancy_level: {icon: 'mdi:motion-sensor'},
occupancy_sensitivity: {icon: 'mdi:motion-sensor'},
occupancy_timeout: {entity_category: 'config', icon: 'mdi:timer'},
overload_protection: {icon: 'mdi:flash'},
pm10: {device_class: 'pm10', state_class: 'measurement'},
@@ -801,7 +828,7 @@ export default class HomeAssistant extends Extension {
power: {device_class: 'power', entity_category: 'diagnostic', state_class: 'measurement'},
power_factor: {device_class: 'power_factor', enabled_by_default: false,
entity_category: 'diagnostic', state_class: 'measurement'},
power_outage_count: {icon: 'mdi:counter'},
power_outage_count: {icon: 'mdi:counter', enabled_by_default: false},
precision: {entity_category: 'config', icon: 'mdi:decimal-comma-increase'},
pressure: {device_class: 'atmospheric_pressure', state_class: 'measurement'},
presence_timeout: {entity_category: 'config', icon: 'mdi:timer'},
@@ -818,7 +845,9 @@ export default class HomeAssistant extends Extension {
temperature_calibration: {entity_category: 'config', icon: 'mdi:wrench-clock'},
temperature_max: {entity_category: 'config', icon: 'mdi:thermometer-plus'},
temperature_min: {entity_category: 'config', icon: 'mdi:thermometer-minus'},
temperature_offset: {icon: 'mdi:thermometer-lines'},
transition: {entity_category: 'config', icon: 'mdi:transition'},
trigger_count: {icon: 'mdi:counter', enabled_by_default: false},
voc: {device_class: 'volatile_organic_compounds', state_class: 'measurement'},
voc_index: {state_class: 'measurement'},
vibration_timeout: {entity_category: 'config', icon: 'mdi:timer'},
@@ -891,9 +920,6 @@ export default class HomeAssistant extends Extension {
discoveryEntry.discovery_payload.entity_category = 'diagnostic';
}
// Let Home Assistant generate entity name when device_class is present
if (discoveryEntry.discovery_payload.device_class) delete discoveryEntry.discovery_payload.name;
discoveryEntries.push(discoveryEntry);
/**
@@ -924,9 +950,6 @@ export default class HomeAssistant extends Extension {
delete discoveryEntry.discovery_payload.device_class;
}
// Let Home Assistant generate entity name when device_class is present
if (discoveryEntry.discovery_payload.device_class) delete discoveryEntry.discovery_payload.name;
if (firstExpose.value_min != null) discoveryEntry.discovery_payload.min = firstExpose.value_min;
if (firstExpose.value_max != null) discoveryEntry.discovery_payload.max = firstExpose.value_max;
@@ -946,6 +969,7 @@ export default class HomeAssistant extends Extension {
effect: {enabled_by_default: false, icon: 'mdi:palette'},
force: {entity_category: 'config', icon: 'mdi:valve'},
keep_time: {entity_category: 'config', icon: 'mdi:av-timer'},
identify: {device_class: 'identify'},
keypad_lockout: {entity_category: 'config', icon: 'mdi:lock'},
load_detection_mode: {entity_category: 'config', icon: 'mdi:tune'},
load_dimmable: {entity_category: 'config', icon: 'mdi:chart-bell-curve'},
@@ -960,6 +984,7 @@ export default class HomeAssistant extends Extension {
power_outage_memory: {entity_category: 'config', icon: 'mdi:power-settings'},
power_supply_mode: {entity_category: 'config', icon: 'mdi:power-settings'},
power_type: {entity_category: 'config', icon: 'mdi:lightning-bolt-circle'},
restart: {device_class: 'restart'},
sensitivity: {entity_category: 'config', icon: 'mdi:tune'},
sensor: {icon: 'mdi:tune'},
sensors_type: {entity_category: 'config', icon: 'mdi:tune'},
@@ -969,6 +994,7 @@ export default class HomeAssistant extends Extension {
temperature_display_mode: {entity_category: 'config', icon: 'mdi:thermometer'},
temperature_sensor_select: {entity_category: 'config', icon: 'mdi:home-thermometer'},
thermostat_unit: {entity_category: 'config', icon: 'mdi:thermometer'},
update: {device_class: 'update'},
volume: {entity_category: 'config', icon: 'mdi: volume-high'},
week: {entity_category: 'config', icon: 'mdi:calendar-clock'},
};
@@ -1008,6 +1034,29 @@ export default class HomeAssistant extends Extension {
command_topic: true,
command_topic_postfix: firstExpose.property,
options: firstExpose.values.map((v) => v.toString()),
enabled_by_default: firstExpose.values.length !== 1, // hide if button is exposed
...lookup[firstExpose.name],
},
});
}
/**
* If enum has only item and only supports SET then expose as button entity.
* Note: select entity is hidden by default to avoid breaking changes
* for selects already existing in HA (legacy).
*/
if (firstExpose.access & ACCESS_SET && firstExpose.values.length === 1) {
discoveryEntries.push({
type: 'button',
object_id: firstExpose.property,
mockProperties: [],
discovery_payload: {
name: endpoint ? `${firstExpose.label} ${endpoint}` : firstExpose.label,
state_topic: false,
command_topic_prefix: endpoint,
command_topic: true,
command_topic_postfix: firstExpose.property,
payload_press: firstExpose.values[0].toString(),
...lookup[firstExpose.name],
},
});
@@ -1075,12 +1124,20 @@ export default class HomeAssistant extends Extension {
}
});
discoveryEntries.forEach((d) => {
// Let Home Assistant generate entity name when device_class is present
if (d.discovery_payload.device_class) {
delete d.discovery_payload.name;
}
});
return discoveryEntries;
}
@bind onDeviceRemoved(data: eventdata.DeviceRemoved): void {
logger.debug(`Clearing Home Assistant discovery topic for '${data.name}'`);
this.discovered[data.ieeeAddr]?.topics.forEach((topic) => {
logger.debug(`Clearing Home Assistant discovery for '${data.name}'`);
const discovered = this.getDiscovered(data.ieeeAddr);
Object.keys(discovered.messages).forEach((topic) => {
this.mqtt.publish(topic, null, {retain: true, qos: 1}, this.discoveryTopic, false, false);
});
@@ -1088,7 +1145,7 @@ export default class HomeAssistant extends Extension {
}
@bind onGroupMembersChanged(data: eventdata.GroupMembersChanged): void {
this.discover(data.group, true);
this.discover(data.group);
}
@bind async onPublishEntityState(data: eventdata.PublishEntityState): Promise<void> {
@@ -1101,8 +1158,9 @@ export default class HomeAssistant extends Extension {
* zigbee2mqtt/mydevice/l1.
*/
const entity = this.zigbee.resolveEntity(data.entity.name);
if (entity.isDevice() && this.discovered[entity.ieeeAddr]) {
for (const objectID of this.discovered[entity.ieeeAddr].objectIDs) {
if (entity.isDevice()) {
Object.keys(this.getDiscovered(entity).messages).forEach((topic) => {
const objectID = topic.match(this.discoveryRegexWoTopic)?.[3];
const lightMatch = /^light_(.*)/.exec(objectID);
const coverMatch = /^cover_(.*)/.exec(objectID);
@@ -1119,11 +1177,9 @@ export default class HomeAssistant extends Extension {
}
}
await this.mqtt.publish(
`${data.entity.name}/${endpoint}`, stringify(payload), {},
);
this.mqtt.publish(`${data.entity.name}/${endpoint}`, stringify(payload), {});
}
}
});
}
/**
@@ -1160,20 +1216,21 @@ export default class HomeAssistant extends Extension {
// Clear before rename so Home Assistant uses new friendly_name
// https://github.com/Koenkk/zigbee2mqtt/issues/4096#issuecomment-674044916
if (data.homeAssisantRename) {
for (const config of this.getConfigs(data.entity)) {
const topic = this.getDiscoveryTopic(config, data.entity);
const discovered = this.getDiscovered(data.entity);
for (const topic of Object.keys(discovered.messages)) {
this.mqtt.publish(topic, null, {retain: true, qos: 1}, this.discoveryTopic, false, false);
}
discovered.messages = {};
// Make sure Home Assistant deletes the old entity first otherwise another one (_2) is created
// https://github.com/Koenkk/zigbee2mqtt/issues/12610
await utils.sleep(2);
}
this.discover(data.entity, true);
this.discover(data.entity);
if (data.entity.isDevice() && this.discoveredTriggers[data.entity.ieeeAddr]) {
for (const config of this.discoveredTriggers[data.entity.ieeeAddr]) {
if (data.entity.isDevice()) {
for (const config of this.getDiscovered(data.entity).triggers) {
const key = config.substring(0, config.indexOf('_'));
const value = config.substring(config.indexOf('_') + 1);
this.publishDeviceTriggerDiscover(data.entity, key, value, true);
@@ -1357,28 +1414,22 @@ export default class HomeAssistant extends Extension {
return configs;
}
private getDiscoverKey(entity: Device | Group | Bridge): string | number {
return entity.ID;
}
private discover(entity: Device | Group | Bridge, force=false): void {
// Check if already discovered and check if there are configs.
const discoverKey = this.getDiscoverKey(entity);
const discover = force || !this.discovered[discoverKey];
private discover(entity: Device | Group | Bridge, publish: boolean = true): void {
// Handle type differences.
const isDevice = entity.isDevice();
const isGroup = entity.isGroup();
if (isGroup && (!discover || entity.zh.members.length === 0)) {
if (isGroup && entity.zh.members.length === 0) {
return;
} else if (isDevice && (!discover || !entity.definition || entity.zh.interviewing ||
} else if (isDevice && (!entity.definition || entity.zh.interviewing ||
(entity.options.hasOwnProperty('homeassistant') && !entity.options.homeassistant))) {
return;
}
const lastDiscovered = this.discovered[discoverKey];
this.discovered[discoverKey] = {topics: new Set(), mockProperties: new Set(), objectIDs: new Set()};
const discovered = this.getDiscovered(entity);
discovered.discovered = true;
const lastDiscoverdTopics = Object.keys(discovered.messages);
const newDiscoveredTopics: Set<string> = new Set();
this.getConfigs(entity).forEach((config) => {
const payload = {...config.discovery_payload};
const baseTopic = `${settings.get().mqtt.base_topic}/${entity.name}`;
@@ -1581,22 +1632,31 @@ export default class HomeAssistant extends Extension {
}
const topic = this.getDiscoveryTopic(config, entity);
this.mqtt.publish(topic, stringify(payload), {retain: true, qos: 1}, this.discoveryTopic, false, false);
this.discovered[discoverKey].topics.add(topic);
this.discovered[discoverKey].objectIDs.add(config.object_id);
config.mockProperties?.forEach((mockProperty) =>
this.discovered[discoverKey].mockProperties.add(mockProperty));
const payloadStr = stringify(payload);
newDiscoveredTopics.add(topic);
// Only discover when not discovered yet
const discoveredMessage = discovered.messages[topic];
if (!discoveredMessage || discoveredMessage.payload !== payloadStr || !discoveredMessage.published) {
discovered.messages[topic] = {payload: payloadStr, published: publish};
if (publish) {
this.mqtt.publish(topic, payloadStr, {retain: true, qos: 1}, this.discoveryTopic, false, false);
}
} else {
logger.debug(`Skipping discovery of '${topic}', already discovered`);
}
config.mockProperties?.forEach((mockProperty) => discovered.mockProperties.add(mockProperty));
});
lastDiscovered?.topics?.forEach((topic) => {
if (!this.discovered[discoverKey].topics.has(topic)) {
lastDiscoverdTopics.forEach((topic) => {
const isDeviceAutomation = topic.match(this.discoveryRegexWoTopic)[1] === 'device_automation';
if (!newDiscoveredTopics.has(topic) && !isDeviceAutomation) {
this.mqtt.publish(topic, null, {retain: true, qos: 1}, this.discoveryTopic, false, false);
}
});
}
@bind private onMQTTMessage(data: eventdata.MQTTMessage): void {
const discoveryRegex = new RegExp(`${this.discoveryTopic}/(.*)/(.*)/(.*)/config`);
const discoveryMatch = data.topic.match(discoveryRegex);
const discoveryMatch = data.topic.match(this.discoveryRegex);
const isDeviceAutomation = discoveryMatch && discoveryMatch[1] === 'device_automation';
if (discoveryMatch) {
// Clear outdated discovery configs and remember already discovered device_automations
@@ -1626,27 +1686,23 @@ export default class HomeAssistant extends Extension {
const key = `${discoveryMatch[3].substring(0, discoveryMatch[3].indexOf('_'))}`;
const triggerTopic = `${settings.get().mqtt.base_topic}/${entity.name}/${key}`;
if (isDeviceAutomation && message.topic === triggerTopic) {
if (!this.discoveredTriggers[ID]) {
this.discoveredTriggers[ID] = new Set();
}
this.discoveredTriggers[ID].add(discoveryMatch[3]);
this.getDiscovered(ID).triggers.add(discoveryMatch[3]);
}
}
if (!clear && !isDeviceAutomation) {
const type = discoveryMatch[1];
const objectID = discoveryMatch[3];
clear = !this.getConfigs(entity)
.find((c) => c.type === type && c.object_id === objectID &&
`${this.discoveryTopic}/${this.getDiscoveryTopic(c, entity)}` === data.topic);
const topic = data.topic.substring(this.discoveryTopic.length + 1);
if (!clear && !isDeviceAutomation && !(topic in this.getDiscovered(entity).messages)) {
clear = true;
}
// Device was flagged to be excluded from homeassistant discovery
clear = clear || (entity.options.hasOwnProperty('homeassistant') && !entity.options.homeassistant);
if (clear) {
logger.debug(`Clearing Home Assistant config '${data.topic}'`);
const topic = data.topic.substring(this.discoveryTopic.length + 1);
logger.debug(`Clearing outdated Home Assistant config '${data.topic}'`);
this.mqtt.publish(topic, null, {retain: true, qos: 1}, this.discoveryTopic, false, false);
} else {
this.getDiscovered(entity).messages[topic] = {payload: stringify(message), published: true};
}
} else if ((data.topic === this.statusTopic || data.topic === defaultStatusTopic) &&
data.message.toLowerCase() === 'online') {
@@ -1664,17 +1720,21 @@ export default class HomeAssistant extends Extension {
}
@bind onZigbeeEvent(data: {device: Device}): void {
this.discover(data.device);
if (!this.getDiscovered(data.device).discovered) {
this.discover(data.device);
}
}
@bind async onScenesChanged(data: eventdata.ScenesChanged): Promise<void> {
// Re-trigger MQTT discovery of changed devices and groups, similar to bridge.ts
// First, clear existing scene discovery topics
logger.debug(`Clearing Home Assistant scene discovery topics for '${data.entity.name}'`);
this.discovered[this.getDiscoverKey(data.entity)]?.topics.forEach((topic) => {
logger.debug(`Clearing Home Assistant scene discovery for '${data.entity.name}'`);
const discovered = this.getDiscovered(data.entity);
Object.keys(discovered.messages).forEach((topic) => {
if (topic.startsWith('scene')) {
this.mqtt.publish(topic, null, {retain: true, qos: 1}, this.discoveryTopic, false, false);
delete discovered.messages[topic];
}
});
@@ -1685,7 +1745,7 @@ export default class HomeAssistant extends Extension {
// Re-discover entity (including any new scenes).
logger.debug(`Re-discovering entities with their scenes.`);
this.discover(data.entity, true);
this.discover(data.entity);
}
private getDevicePayload(entity: Device | Group | Bridge): KeyValue {
@@ -1735,8 +1795,7 @@ export default class HomeAssistant extends Extension {
}
override adjustMessageBeforePublish(entity: Device | Group | Bridge, message: KeyValue): void {
const discoverKey = this.getDiscoverKey(entity);
this.discovered[discoverKey]?.mockProperties?.forEach((mockProperty) => {
this.getDiscovered(entity).mockProperties.forEach((mockProperty) => {
if (!message.hasOwnProperty(mockProperty.property)) {
message[mockProperty.property] = mockProperty.value;
}
@@ -1774,12 +1833,9 @@ export default class HomeAssistant extends Extension {
return;
}
if (!this.discoveredTriggers[device.ieeeAddr]) {
this.discoveredTriggers[device.ieeeAddr] = new Set();
}
const discovered = this.getDiscovered(device);
const discoveredKey = `${key}_${value}`;
if (this.discoveredTriggers[device.ieeeAddr].has(discoveredKey) && !force) {
if (discovered.triggers.has(discoveredKey) && !force) {
return;
}
@@ -1804,11 +1860,7 @@ export default class HomeAssistant extends Extension {
};
await this.mqtt.publish(topic, stringify(payload), {retain: true, qos: 1}, this.discoveryTopic, false, false);
this.discoveredTriggers[device.ieeeAddr].add(discoveredKey);
}
_clearDiscoveredTrigger(): void {
this.discoveredTriggers = {};
discovered.triggers.add(discoveredKey);
}
private getBridgeEntity(coordinatorVersion: zh.CoordinatorVersion): Bridge {
@@ -1881,7 +1933,7 @@ export default class HomeAssistant extends Extension {
command_topic: `${baseTopic}/request/options`,
command_template:
'{"options": {"advanced": {"log_level": "{{ value }}" } } }',
options: ['info', 'warn', 'error', 'debug'],
options: settings.LOG_LEVELS,
},
},
// Sensors:
+5 -6
View File
@@ -8,7 +8,6 @@ import bind from 'bind-decorator';
const configRegex =
new RegExp(`${settings.get().mqtt.base_topic}/bridge/config/((?:\\w+/get)|(?:\\w+/factory_reset)|(?:\\w+))`);
const allowedLogLevels = ['error', 'warn', 'info', 'debug'];
export default class BridgeLegacy extends Extension {
private lastJoinedDeviceName: string = null;
@@ -118,12 +117,12 @@ export default class BridgeLegacy extends Extension {
}
@bind logLevel(topic: string, message: string): void {
const level = message.toLowerCase() as 'error' | 'warn' | 'info' | 'debug';
if (allowedLogLevels.includes(level)) {
const level = message.toLowerCase() as settings.LogLevel;
if (settings.LOG_LEVELS.includes(level)) {
logger.info(`Switching log level to '${level}'`);
logger.setLevel(level);
} else {
logger.error(`Could not set log level to '${level}'. Allowed level: '${allowedLogLevels.join(',')}'`);
logger.error(`Could not set log level to '${level}'. Allowed level: '${settings.LOG_LEVELS.join(',')}'`);
}
this.publish();
@@ -320,7 +319,7 @@ export default class BridgeLegacy extends Extension {
try {
logger.info(`${lookup[action][1]} '${entity.name}'`);
if (action === 'force_remove') {
await entity.zh.removeFromDatabase();
entity.zh.removeFromDatabase();
} else {
await entity.zh.removeFromNetwork();
}
@@ -445,7 +444,7 @@ export default class BridgeLegacy extends Extension {
stringify({type: `touchlink`, message: 'reset_success', meta: {status: 'success'}}),
);
} else {
logger.warn('Failed to factory reset device through Touchlink');
logger.warning('Failed to factory reset device through Touchlink');
this.mqtt.publish(
'bridge/log',
stringify({type: `touchlink`, message: 'reset_failed', meta: {status: 'failed'}}),
+10 -5
View File
@@ -1,7 +1,6 @@
/* istanbul ignore file */
import * as settings from '../../util/settings';
import logger from '../../util/logger';
import utils from '../../util/utils';
import Extension from '../extension';
import bind from 'bind-decorator';
import Device from '../../model/device';
@@ -19,19 +18,25 @@ export default class DeviceGroupMembership extends Extension {
return null;
}
const parsed = utils.parseEntityID(match[1]);
const device = this.zigbee.resolveEntity(parsed.ID) as Device;
const parsed = this.zigbee.resolveEntityAndEndpoint(match[1]);
const device = parsed?.entity as Device;
if (!device || !(device instanceof Device)) {
logger.error(`Device '${match[1]}' does not exist`);
return;
}
const endpoint = device.endpoint(parsed.endpoint);
const endpoint = parsed.endpoint;
if (parsed.endpointID && !endpoint) {
logger.error(`Device '${parsed.ID}' does not have endpoint '${parsed.endpointID}'`);
return;
}
const response = await endpoint.command(
`genGroups`, 'getMembership', {groupcount: 0, grouplist: []}, {},
);
if (!response) {
logger.warn(`Couldn't get group membership of ${device.ieeeAddr}`);
logger.warning(`Couldn't get group membership of ${device.ieeeAddr}`);
return;
}
+4 -4
View File
@@ -39,16 +39,16 @@ export default class SoftReset extends Extension {
}
private async handleTimeout(): Promise<void> {
logger.warn('Soft reset timeout triggered');
logger.warning('Soft reset timeout triggered');
try {
await this.zigbee.reset('soft');
logger.warn('Soft reset ZNP due to timeout');
logger.warning('Soft reset ZNP due to timeout');
} catch (error) {
logger.warn('Soft reset failed, trying stop/start');
logger.warning('Soft reset failed, trying stop/start');
await this.zigbee.stop();
logger.warn('Zigbee stopped');
logger.warning('Zigbee stopped');
try {
await this.zigbee.start();
+7 -3
View File
@@ -65,6 +65,10 @@ export default class OTAUpdate extends Extension {
// remove them.
for (const device of this.zigbee.devices(false)) {
this.removeProgressAndRemainingFromState(device);
// Reset update state, e.g. when Z2M restarted during update.
if (this.state.get(device).update?.state === 'updating') {
this.state.get(device).update.state = 'available';
}
}
}
@@ -94,7 +98,7 @@ export default class OTAUpdate extends Extension {
try {
// @ts-expect-error typing guaranteed by data.type
const dataData: zhc.ota.ImageInfo = data.data;
availableResult = await data.device.definition.ota.isUpdateAvailable(data.device.zh, logger, dataData);
availableResult = await data.device.definition.ota.isUpdateAvailable(data.device.zh, dataData);
} catch (e) {
supportsOTA = false;
logger.debug(`Failed to check if update available for '${data.device.name}' (${e.message})`);
@@ -202,7 +206,7 @@ export default class OTAUpdate extends Extension {
}
try {
const availableResult = await device.definition.ota.isUpdateAvailable(device.zh, logger, null);
const availableResult = await device.definition.ota.isUpdateAvailable(device.zh, null);
const msg = `${availableResult.available ? 'Update' : 'No update'} available for '${device.name}'`;
logger.info(msg);
@@ -266,7 +270,7 @@ export default class OTAUpdate extends Extension {
};
const from_ = await this.readSoftwareBuildIDAndDateCode(device, 'immediate');
const fileVersion = await device.definition.ota.updateToLatest(device.zh, logger, onProgress);
const fileVersion = await device.definition.ota.updateToLatest(device.zh, onProgress);
logger.info(`Finished update of '${device.name}'`);
this.eventBus.emitReconfigure({device});
this.removeProgressAndRemainingFromState(device);
+26 -16
View File
@@ -10,8 +10,13 @@ import Group from '../model/group';
import Device from '../model/device';
import bind from 'bind-decorator';
const topicRegex = new RegExp(`^(.+?)(?:/(${utils.endpointNames.join('|')}|\\d+))?/(get|set)(?:/(.+))?`);
const propertyEndpointRegex = new RegExp(`^(.*?)_(${utils.endpointNames.join('|')})$`);
let topicGetSetRegex: RegExp;
// Used by `publish.test.js` to reload regex when changing `mqtt.base_topic`.
export const loadTopicGetSetRegex = (): void => {
topicGetSetRegex = new RegExp(`^${settings.get().mqtt.base_topic}/(?!bridge)(.+?)/(get|set)(?:/(.+))?$`);
};
loadTopicGetSetRegex();
const stateValues = ['on', 'off', 'toggle', 'open', 'close', 'stop', 'lock', 'unlock'];
const sceneConverterKeys = ['scene_store', 'scene_add', 'scene_remove', 'scene_remove_all', 'scene_rename'];
@@ -40,18 +45,24 @@ export default class Publish extends Extension {
}
parseTopic(topic: string): ParsedTopic | null {
const match = topic.match(topicRegex);
if (!match) {
return null;
}
// The function supports the following topic formats (below are for 'set'. 'get' will look the same):
// - <base_topic>/device_name/set (endpoint and attribute is defined in the payload)
// - <base_topic>/device_name/set/attribute (default endpoint used)
// - <base_topic>/device_name/endpoint/set (attribute is defined in the payload)
// - <base_topic>/device_name/endpoint/set/attribute (payload is the value)
const ID = match[1].replace(`${settings.get().mqtt.base_topic}/`, '');
// If we didn't replace base_topic we received something we don't care about
if (ID === match[1] || ID.match(/bridge/)) {
return null;
}
// Make the rough split on get/set keyword.
// Before the get/set is the device name and optional endpoint name.
// After it there will be an optional attribute name.
const match = topic.match(topicGetSetRegex);
if (!match) return null;
return {ID: ID, endpoint: match[2], type: match[3] as 'get' | 'set', attribute: match[4]};
const deviceNameAndEndpoint = match[1];
const attribute = match[3];
// Now parse the device/group name, and endpoint name
const entity = this.zigbee.resolveEntityAndEndpoint(deviceNameAndEndpoint);
return {ID: entity.ID, endpoint: entity.endpointID, type: match[2] as 'get' | 'set', attribute: attribute};
}
parseMessage(parsedTopic: ParsedTopic, data: eventdata.MQTTMessage): KeyValue | null {
@@ -185,6 +196,9 @@ export default class Publish extends Extension {
toPublish[ID] = {...toPublish[ID], ...payload};
};
const endpointNames = re instanceof Device ? re.getEndpointNames() : [];
const propertyEndpointRegex = new RegExp(`^(.*?)_(${endpointNames.join('|')})$`);
for (let [key, value] of entries) {
let endpointName = parsedTopic.endpoint;
let localTarget = target;
@@ -196,10 +210,6 @@ export default class Publish extends Extension {
endpointName = propertyEndpointMatch[2];
key = propertyEndpointMatch[1];
localTarget = re.endpoint(endpointName);
if (localTarget == null) {
logger.error(`Device '${re.name}' has no endpoint '${endpointName}'`);
continue;
}
endpointOrGroupID = localTarget.ID;
}
+1 -1
View File
@@ -118,7 +118,7 @@ export default class Receive extends Extension {
// for e.g. click switches where we need to count number of clicks and detect long presses.
const publish = (payload: KeyValue): void => {
const options: KeyValue = data.device.options;
zhc.postProcessConvertedFromZigbeeMessage(data.device.definition, payload, options, logger);
zhc.postProcessConvertedFromZigbeeMessage(data.device.definition, payload, options);
if (settings.get().advanced.elapsed) {
const now = Date.now();
+16 -3
View File
@@ -1,6 +1,7 @@
/* eslint-disable brace-style */
import * as settings from '../util/settings';
import * as zhc from 'zigbee-herdsman-converters';
import {CustomClusters} from 'zigbee-herdsman/dist/zspec/zcl/definition/tstype';
export default class Device {
public zh: zh.Device;
@@ -16,6 +17,9 @@ export default class Device {
get isSupported(): boolean {
return this.zh.type === 'Coordinator' || (this.definition && !this.definition.generated);
}
get customClusters(): CustomClusters {
return this.zh.customClusters;
}
constructor(device: zh.Device) {
this.zh = device;
@@ -65,12 +69,21 @@ export default class Device {
}
endpointName(endpoint: zh.Endpoint): string {
let name = null;
let epName = null;
if (this.definition?.endpoint) {
name = Object.entries(this.definition?.endpoint(this.zh)).find((e) => e[1] == endpoint.ID)[0];
const mapping = this.definition?.endpoint(this.zh);
for (const [name, id] of Object.entries(mapping)) {
if (id == endpoint.ID) {
epName = name;
}
}
}
/* istanbul ignore next */
return name === 'default' ? null : name;
return epName === 'default' ? null : epName;
}
getEndpointNames(): string[] {
return Object.keys(this.definition?.endpoint?.(this.zh) ?? {}).filter((name) => name !== 'default');
}
isIkeaTradfri(): boolean {return this.zh.manufacturerID === 4476;}
+25 -18
View File
@@ -6,6 +6,8 @@ import fs from 'fs';
import bind from 'bind-decorator';
import type {QoS} from 'mqtt-packet';
const NS = 'z2m:mqtt';
export default class MQTT {
private publishedTopics: Set<string> = new Set();
private connectionTimer: NodeJS.Timeout;
@@ -22,7 +24,7 @@ export default class MQTT {
async connect(): Promise<void> {
const mqttSettings = settings.get().mqtt;
logger.info(`Connecting to MQTT server at ${mqttSettings.server}`);
logger.info(`Connecting to MQTT server at ${mqttSettings.server}`, NS);
const options: mqtt.IClientOptions = {
will: {
@@ -38,37 +40,37 @@ export default class MQTT {
}
if (mqttSettings.keepalive) {
logger.debug(`Using MQTT keepalive: ${mqttSettings.keepalive}`);
logger.debug(`Using MQTT keepalive: ${mqttSettings.keepalive}`, NS);
options.keepalive = mqttSettings.keepalive;
}
if (mqttSettings.ca) {
logger.debug(`MQTT SSL/TLS: Path to CA certificate = ${mqttSettings.ca}`);
logger.debug(`MQTT SSL/TLS: Path to CA certificate = ${mqttSettings.ca}`, NS);
options.ca = fs.readFileSync(mqttSettings.ca);
}
if (mqttSettings.key && mqttSettings.cert) {
logger.debug(`MQTT SSL/TLS: Path to client key = ${mqttSettings.key}`);
logger.debug(`MQTT SSL/TLS: Path to client certificate = ${mqttSettings.cert}`);
logger.debug(`MQTT SSL/TLS: Path to client key = ${mqttSettings.key}`, NS);
logger.debug(`MQTT SSL/TLS: Path to client certificate = ${mqttSettings.cert}`, NS);
options.key = fs.readFileSync(mqttSettings.key);
options.cert = fs.readFileSync(mqttSettings.cert);
}
if (mqttSettings.user && mqttSettings.password) {
logger.debug(`Using MQTT login with username: ${mqttSettings.user}`);
logger.debug(`Using MQTT login with username: ${mqttSettings.user}`, NS);
options.username = mqttSettings.user;
options.password = mqttSettings.password;
} else {
logger.debug(`Using MQTT anonymous login`);
logger.debug(`Using MQTT anonymous login`, NS);
}
if (mqttSettings.client_id) {
logger.debug(`Using MQTT client ID: '${mqttSettings.client_id}'`);
logger.debug(`Using MQTT client ID: '${mqttSettings.client_id}'`, NS);
options.clientId = mqttSettings.client_id;
}
if (mqttSettings.hasOwnProperty('reject_unauthorized') && !mqttSettings.reject_unauthorized) {
logger.debug(`MQTT reject_unauthorized set false, ignoring certificate warnings.`);
logger.debug(`MQTT reject_unauthorized set false, ignoring certificate warnings.`, NS);
options.rejectUnauthorized = false;
}
@@ -85,7 +87,7 @@ export default class MQTT {
});
this.client.on('error', (err) => {
logger.error(`MQTT error: ${err.message}`);
logger.error(`MQTT error: ${err.message}`, NS);
reject(err);
});
this.client.on('message', this.onMessage);
@@ -97,11 +99,11 @@ export default class MQTT {
clearTimeout(this.connectionTimer);
this.connectionTimer = setInterval(() => {
if (this.client.reconnecting) {
logger.error('Not connected to MQTT server!');
logger.error('Not connected to MQTT server!', NS);
}
}, utils.seconds(10));
logger.info('Connected to MQTT server');
logger.info('Connected to MQTT server', NS);
await this.publishStateOnline();
if (!this.initialConnect) {
@@ -126,18 +128,22 @@ export default class MQTT {
await this.publish('bridge/state', utils.availabilityPayload('offline', settings.get()),
{retain: true, qos: 0});
this.eventBus.removeListeners(this);
logger.info('Disconnecting from MQTT server');
this.client.end();
logger.info('Disconnecting from MQTT server', NS);
this.client?.end();
}
subscribe(topic: string): void {
this.client.subscribe(topic);
}
unsubscribe(topic: string): void {
this.client.unsubscribe(topic);
}
@bind public onMessage(topic: string, message: Buffer): void {
// Since we subscribe to zigbee2mqtt/# we also receive the message we send ourselves, skip these.
if (!this.publishedTopics.has(topic)) {
logger.debug(`Received MQTT message on '${topic}' with data '${message.toString()}'`);
logger.debug(`Received MQTT message on '${topic}' with data '${message.toString()}'`, NS);
this.eventBus.emitMQTTMessage({topic, message: message.toString()});
}
@@ -173,15 +179,16 @@ export default class MQTT {
this.eventBus.emitMQTTMessagePublished({topic, payload, options: {...defaultOptions, ...options}});
if (!this.isConnected()) {
/* istanbul ignore else */
if (!skipLog) {
logger.error(`Not connected to MQTT server!`);
logger.error(`Cannot send message: topic: '${topic}', payload: '${payload}`);
logger.error(`Not connected to MQTT server!`, NS);
logger.error(`Cannot send message: topic: '${topic}', payload: '${payload}`, NS);
}
return;
}
if (!skipLog) {
logger.info(`MQTT publish: topic '${topic}', payload '${payload}'`);
logger.info(`MQTT publish: topic '${topic}', payload '${payload}'`, NS);
}
const actualOptions: mqtt.IClientPublishOptions = {...defaultOptions, ...options};
+7 -6
View File
@@ -1,4 +1,5 @@
/* eslint-disable camelcase */
import {LogLevel} from 'lib/util/settings';
import type {
Device as ZHDevice,
Group as ZHGroup,
@@ -15,11 +16,8 @@ import type {
import type {
Cluster as ZHCluster,
} from 'zigbee-herdsman/dist/zcl/tstype';
import type {
FrameControl as ZHFrameControl,
} from 'zigbee-herdsman/dist/zcl';
} from 'zigbee-herdsman/dist/zspec/zcl/definition/tstype';
import type * as zhc from 'zigbee-herdsman-converters';
@@ -143,7 +141,7 @@ declare global {
serial: {
disable_led: boolean,
port?: string,
adapter?: 'deconz' | 'zstack' | 'ezsp' | 'zigate',
adapter?: 'deconz' | 'zstack' | 'ezsp' | 'zigate' | 'ember',
baudrate?: number,
rtscts?: boolean,
},
@@ -194,8 +192,11 @@ declare global {
log_output: ('console' | 'file' | 'syslog')[],
log_directory: string,
log_file: string,
log_level: 'debug' | 'info' | 'error' | 'warn',
log_level: LogLevel,
log_namespaced_levels: Record<string, LogLevel>,
log_syslog: KeyValue,
log_debug_to_mqtt_frontend: boolean,
log_debug_namespace_ignore: string,
pan_id: number | 'GENERATE',
ext_pan_id: number[] | 'GENERATE',
channel: number,
+205 -200
View File
@@ -7,219 +7,224 @@ import fx from 'mkdir-recursive';
import {rimrafSync} from 'rimraf';
import assert from 'assert';
const colorizer = winston.format.colorize();
class Logger {
private level: settings.LogLevel;
private output: string[];
private directory: string;
private logger: winston.Logger;
private fileTransport: winston.transports.FileTransportInstance;
private debugNamespaceIgnoreRegex?: RegExp;
private namespacedLevels: Record<string, settings.LogLevel>;
type Z2MLogLevel = 'warn' | 'debug' | 'info' | 'error';
type WinstonLogLevel = 'warning' | 'debug' | 'info' | 'error';
public init(): void {
// What transports to enable
this.output = settings.get().advanced.log_output;
// Directory to log to
const timestamp = moment(Date.now()).format('YYYY-MM-DD.HH-mm-ss');
this.directory = settings.get().advanced.log_directory.replace('%TIMESTAMP%', timestamp);
const logFilename = settings.get().advanced.log_file.replace('%TIMESTAMP%', timestamp);
this.level = settings.get().advanced.log_level;
this.namespacedLevels = settings.get().advanced.log_namespaced_levels;
const z2mToWinstonLevel = (level: Z2MLogLevel): WinstonLogLevel => level === 'warn' ? 'warning' : level;
const winstonToZ2mLevel = (level: WinstonLogLevel): Z2MLogLevel => level === 'warning' ? 'warn' : level;
assert(
settings.LOG_LEVELS.includes(this.level),
`'${this.level}' is not valid log_level, use one of '${settings.LOG_LEVELS.join(', ')}'`,
);
const levelWithCompensatedLength: {[s: string]: string} = {
'info': 'info ',
'error': 'error',
'warn': 'warn ',
'debug': 'debug',
};
const timestampFormat = (): string => moment().format(settings.get().advanced.timestamp_format);
let logger: winston.Logger;
let fileTransport : winston.transport;
let output: string[];
let directory: string;
let logFilename: string;
let transportsToUse: winston.transport[];
function init(): void {
// What transports to enable
output = settings.get().advanced.log_output;
// Directory to log to
const timestamp = moment(Date.now()).format('YYYY-MM-DD.HH-mm-ss');
directory = settings.get().advanced.log_directory.replace('%TIMESTAMP%', timestamp);
logFilename = settings.get().advanced.log_file.replace('%TIMESTAMP%', timestamp);
// Make sure that log directory exists when not logging to stdout only
if (output.includes('file')) {
fx.mkdirSync(directory);
if (settings.get().advanced.log_symlink_current) {
const current = settings.get().advanced.log_directory.replace('%TIMESTAMP%', 'current');
const actual = './' + timestamp;
/* istanbul ignore next */
if (fs.existsSync(current)) {
fs.unlinkSync(current);
}
fs.symlinkSync(actual, current);
}
}
// Determine the log level.
const z2mLevel = settings.get().advanced.log_level;
const validLevels = ['info', 'error', 'warn', 'debug'];
assert(validLevels.includes(z2mLevel),
`'${z2mLevel}' is not valid log_level, use one of '${validLevels.join(', ')}'`);
const level = z2mToWinstonLevel(z2mLevel);
const timestampFormat = (): string => moment().format(settings.get().advanced.timestamp_format);
// Setup default console logger
transportsToUse = [
new winston.transports.Console({
level,
silent: !output.includes('console'),
this.logger = winston.createLogger({
level: 'debug',
format: winston.format.combine(
winston.format.errors({stack: true}),
winston.format.timestamp({format: timestampFormat}),
winston.format.printf(/* istanbul ignore next */(info) => {
const {timestamp, level, message} = info;
const l = winstonToZ2mLevel(level as WinstonLogLevel);
),
levels: winston.config.syslog.levels,
});
const plainPrefix = `Zigbee2MQTT:${levelWithCompensatedLength[l]}`;
let prefix = plainPrefix;
if (process.stdout.isTTY) {
prefix = colorizer.colorize(l, plainPrefix);
}
return `${prefix} ${timestamp.split('.')[0]}: ${message}`;
const consoleSilenced = !this.output.includes('console');
// Print to user what logging is active
let logging = `Logging to console${consoleSilenced ? ' (silenced)' : ''}`;
// Setup default console logger
this.logger.add(new winston.transports.Console({
silent: consoleSilenced,
// winston.config.syslog.levels sets 'warning' as 'red'
format: winston.format.combine(
winston.format.colorize({colors: {debug: 'blue', info: 'green', warning: 'yellow', error: 'red'}}),
winston.format.printf(/* istanbul ignore next */(info) => {
return `[${info.timestamp}] ${info.level}: \t${info.namespace}: ${info.message}`;
}),
),
}),
];
}));
// Add file logger when enabled
// NOTE: the initiation of the logger, even when not added as transport tries to create the logging directory
const transportFileOptions: KeyValue = {
filename: path.join(directory, logFilename),
json: false,
level,
format: winston.format.combine(
winston.format.timestamp({format: timestampFormat}),
winston.format.printf(/* istanbul ignore next */(info) => {
const {timestamp, level, message} = info;
const l = winstonToZ2mLevel(level as WinstonLogLevel);
return `${levelWithCompensatedLength[l]} ${timestamp.split('.')[0]}: ${message}`;
}),
),
};
if (this.output.includes('file')) {
logging += `, file (filename: ${logFilename})`;
if (settings.get().advanced.log_rotation) {
transportFileOptions.tailable = true;
transportFileOptions.maxFiles = 3; // Keep last 3 files
transportFileOptions.maxsize = 10000000; // 10MB
}
// Make sure that log directory exists when not logging to stdout only
fx.mkdirSync(this.directory);
if (output.includes('file')) {
fileTransport = new winston.transports.File(transportFileOptions);
transportsToUse.push(fileTransport);
}
/* istanbul ignore next */
if (output.includes('syslog')) {
// eslint-disable-next-line
require('winston-syslog').Syslog;
const options: KeyValue = {
app_name: 'Zigbee2MQTT',
format: winston.format.printf(/* istanbul ignore next */(info) => {
return `${info.message}`;
}),
...settings.get().advanced.log_syslog,
};
if (options.hasOwnProperty('type')) options.type = options.type.toString();
// @ts-ignore
transportsToUse.push(new winston.transports.Syslog(options));
}
logger = winston.createLogger({transports: transportsToUse, levels: winston.config.syslog.levels});
}
// Cleanup any old log directory.
function cleanup(): void {
if (settings.get().advanced.log_directory.includes('%TIMESTAMP%')) {
const rootDirectory = path.join(directory, '..');
let directories = fs.readdirSync(rootDirectory).map((d) => {
d = path.join(rootDirectory, d);
return {path: d, birth: fs.statSync(d).mtime};
});
directories.sort((a: KeyValue, b: KeyValue) => b.birth - a.birth);
directories = directories.slice(10, directories.length);
directories.forEach((dir) => {
logger.debug(`Removing old log directory '${dir.path}'`);
rimrafSync(dir.path);
});
}
}
// Print to user what logging is enabled
function logOutput(): void {
if (output.includes('file')) {
if (output.includes('console')) {
logger.info(`Logging to console and directory: '${directory}' filename: ${logFilename}`);
} else {
logger.info(`Logging to directory: '${directory}' filename: ${logFilename}`);
}
cleanup();
} else if (output.includes('console')) {
logger.info(`Logging to console only'`);
}
}
function addTransport(transport: winston.transport): void {
transport.level = transportsToUse[0].level;
logger.add(transport);
}
function getLevel(): Z2MLogLevel {
return winstonToZ2mLevel(transportsToUse[0].level as WinstonLogLevel);
}
function setLevel(level: Z2MLogLevel): void {
logger.transports.forEach((transport) => transport.level = z2mToWinstonLevel(level as Z2MLogLevel));
}
function warn(message: string): void {
// winston.config.syslog.levels doesn't have warn, but is required for syslog.
logger.warning(message);
}
function warning(message: string): void {
logger.warning(message);
}
function info(message: string): void {
logger.info(message);
}
function debug(message: string): void {
logger.debug(message);
}
function error(message: string): void {
logger.error(message);
}
// Workaround for https://github.com/winstonjs/winston/issues/1629.
// https://github.com/Koenkk/zigbee2mqtt/pull/10905
/* istanbul ignore next */
async function end(): Promise<void> {
logger.end();
await new Promise<void>((resolve) => {
if (!fileTransport) {
process.nextTick(resolve);
} else {
// @ts-ignore
if (fileTransport._dest) {
// @ts-ignore
fileTransport._dest.on('finish', resolve);
} else {
// @ts-ignore
fileTransport.on('open', () => fileTransport._dest.on('finish', resolve));
if (settings.get().advanced.log_symlink_current) {
const current = settings.get().advanced.log_directory.replace('%TIMESTAMP%', 'current');
const actual = './' + timestamp;
/* istanbul ignore next */
if (fs.existsSync(current)) {
fs.unlinkSync(current);
}
fs.symlinkSync(actual, current);
}
// Add file logger when enabled
// eslint-disable-next-line max-len
// NOTE: the initiation of the logger even when not added as transport tries to create the logging directory
const transportFileOptions: winston.transports.FileTransportOptions = {
filename: path.join(this.directory, logFilename),
format: winston.format.printf(/* istanbul ignore next */(info) => {
return `[${info.timestamp}] ${info.level}: \t${info.namespace}: ${info.message}`;
}),
};
if (settings.get().advanced.log_rotation) {
transportFileOptions.tailable = true;
transportFileOptions.maxFiles = 3; // Keep last 3 files
transportFileOptions.maxsize = 10000000; // 10MB
}
this.fileTransport = new winston.transports.File(transportFileOptions);
this.logger.add(this.fileTransport);
}
});
/* istanbul ignore next */
if (this.output.includes('syslog')) {
logging += `, syslog`;
// eslint-disable-next-line
require('winston-syslog').Syslog;
const options: KeyValue = {
app_name: 'Zigbee2MQTT',
format: winston.format.printf(/* istanbul ignore next */(info) => {
return `${info.namespace}: ${info.message}`;
}),
...settings.get().advanced.log_syslog,
};
if (options.hasOwnProperty('type')) {
options.type = options.type.toString();
}
// @ts-expect-error untyped transport
this.logger.add(new winston.transports.Syslog(options));
}
this.setDebugNamespaceIgnore(settings.get().advanced.log_debug_namespace_ignore);
this.info(logging);
}
get winston(): winston.Logger {
return this.logger;
}
public addTransport(transport: winston.transport): void {
this.logger.add(transport);
}
public removeTransport(transport: winston.transport): void {
this.logger.remove(transport);
}
public getDebugNamespaceIgnore(): string {
return this.debugNamespaceIgnoreRegex?.toString().slice(1, -1)/* remove slashes */ ?? '';
}
public setDebugNamespaceIgnore(value: string): void {
this.debugNamespaceIgnoreRegex = value != '' ? new RegExp(value) : undefined;
}
public getLevel(): settings.LogLevel {
return this.level;
}
public setLevel(level: settings.LogLevel): void {
this.level = level;
}
public getNamespacedLevels(): Record<string, settings.LogLevel> {
return this.namespacedLevels;
}
public setNamespacedLevels(nsLevels: Record<string, settings.LogLevel>): void {
this.namespacedLevels = nsLevels;
}
private log(level: settings.LogLevel, message: string, namespace: string): void {
const nsLevel = this.namespacedLevels[namespace] ?? this.level;
if (settings.LOG_LEVELS.indexOf(level) <= settings.LOG_LEVELS.indexOf(nsLevel)) {
this.logger.log(level, message, {namespace});
}
}
public error(message: string, namespace: string = 'z2m'): void {
this.log('error', message, namespace);
}
public warning(message: string, namespace: string = 'z2m'): void {
this.log('warning', message, namespace);
}
public info(message: string, namespace: string = 'z2m'): void {
this.log('info', message, namespace);
}
public debug(message: string, namespace: string = 'z2m'): void {
if (this.debugNamespaceIgnoreRegex?.test(namespace)) {
return;
}
this.log('debug', message, namespace);
}
// Cleanup any old log directory.
public cleanup(): void {
if (settings.get().advanced.log_directory.includes('%TIMESTAMP%')) {
const rootDirectory = path.join(this.directory, '..');
let directories = fs.readdirSync(rootDirectory).map((d) => {
d = path.join(rootDirectory, d);
return {path: d, birth: fs.statSync(d).mtime};
});
directories.sort((a: KeyValue, b: KeyValue) => b.birth - a.birth);
directories = directories.slice(10, directories.length);
directories.forEach((dir) => {
this.debug(`Removing old log directory '${dir.path}'`);
rimrafSync(dir.path);
});
}
}
// Workaround for https://github.com/winstonjs/winston/issues/1629.
// https://github.com/Koenkk/zigbee2mqtt/pull/10905
/* istanbul ignore next */
public async end(): Promise<void> {
this.logger.end();
await new Promise<void>((resolve) => {
if (!this.fileTransport) {
process.nextTick(resolve);
} else {
// @ts-expect-error workaround
if (this.fileTransport._dest) {
// @ts-expect-error workaround
this.fileTransport._dest.on('finish', resolve);
} else {
// @ts-expect-error workaround
this.fileTransport.on('open', () => this.fileTransport._dest.on('finish', resolve));
}
}
});
}
}
export default {
init, logOutput, warn, warning, error, info, debug, setLevel, getLevel, cleanup, addTransport, end,
winston: (): winston.Logger => logger,
};
export default new Logger();
+110 -67
View File
@@ -227,7 +227,7 @@
},
"adapter": {
"type": ["string"],
"enum": ["deconz", "zstack", "zigate", "ezsp", "auto"],
"enum": ["deconz", "zstack", "zigate", "ezsp", "auto", "ember"],
"title": "Adapter",
"default": "auto",
"requiresRestart": true,
@@ -492,79 +492,122 @@
},
"log_level": {
"type": "string",
"enum": ["info", "warn", "error", "debug"],
"enum": ["error", "warning", "info", "debug", "warn"],
"title": "Log level",
"description": "Logging level",
"default": "info"
},
"log_syslog": {
"log_namespaced_levels": {
"type": "object",
"title": "syslog",
"properties": {
"host": {
"type": "string",
"title": "Host",
"description": "The host running syslogd, defaults to localhost.",
"default": "localhost"
"propertyNames": {
"pattern": "^(z2m|zhc|zh)(:[a-z0-9]{1,})*$"
},
"additionalProperties": {
"type": "string",
"enum": ["error", "warning", "info", "debug"]
},
"title": "Log Namespaced Levels",
"description": "Set individual log levels for certain namespaces",
"default": {},
"examples": [
{"z2m:mqtt": "warning"},
{"zh:ember:uart:ash": "info"}
]
},
"log_debug_to_mqtt_frontend": {
"type": "boolean",
"title": "Log debug to MQTT and frontend",
"description": "Log debug level to MQTT and frontend (may decrease overall performance)",
"requiresRestart": true,
"default": false
},
"log_debug_namespace_ignore": {
"type": "string",
"title": "Log debug namespace ignore",
"description": "Do not log these namespaces (regex-based) for debug level",
"default": "",
"examples": [
"^zhc:legacy:fz:(tuya|moes)",
"^zhc:legacy:fz:(tuya|moes)|^zh:ember:uart:|^zh:controller"
]
},
"log_syslog": {
"requiresRestart": true,
"oneOf": [
{
"title": "syslog (disabled)",
"type": "null"
},
"port": {
"type": "number",
"title": "Port",
"description": "The port on the host that syslog is running on, defaults to syslogd's default port.",
"default": 514
},
"protocol": {
"type": "string",
"title": "Protocol",
"description": "The network protocol to log over (e.g. tcp4, udp4, tls4, unix, unix-connect, etc).",
"default": "udp4",
"examples": [
"udp4",
"tls4",
"unix",
"unix-connect"
]
},
"path": {
"type": "string",
"title": "Path",
"description": "The path to the syslog dgram socket (i.e. /dev/log or /var/run/syslog for OS X).",
"default": "/dev/log",
"examples": [
"/var/run/syslog"
]
},
"pid": {
"type": "string",
"title": "PID",
"description": "PID of the process that log messages are coming from (Default process.pid).",
"default": "process.pid"
},
"localhost": {
"type": "string",
"title": "Localhost",
"description": "Host to indicate that log messages are coming from (Default: localhost).",
"default": "localhost"
},
"type": {
"type": "string",
"title": "Type",
"description": "The type of the syslog protocol to use (Default: BSD, also valid: 5424).",
"default": "5424"
},
"app_name": {
"type": "string",
"title": "Localhost",
"description": "The name of the application (Default: Zigbee2MQTT).",
"default": "Zigbee2MQTT"
},
"eol": {
"type": "string",
"title": "eol",
"description": "The end of line character to be added to the end of the message (Default: Message without modifications).",
"default": "/n"
{
"title": "syslog (enabled)",
"type": "object",
"properties": {
"host": {
"type": "string",
"title": "Host",
"description": "The host running syslogd, defaults to localhost.",
"default": "localhost"
},
"port": {
"type": "number",
"title": "Port",
"description": "The port on the host that syslog is running on, defaults to syslogd's default port.",
"default": 514
},
"protocol": {
"type": "string",
"title": "Protocol",
"description": "The network protocol to log over (e.g. tcp4, udp4, tls4, unix, unix-connect, etc).",
"default": "udp4",
"examples": [
"udp4",
"tls4",
"unix",
"unix-connect"
]
},
"path": {
"type": "string",
"title": "Path",
"description": "The path to the syslog dgram socket (i.e. /dev/log or /var/run/syslog for OS X).",
"default": "/dev/log",
"examples": [
"/var/run/syslog"
]
},
"pid": {
"type": "string",
"title": "PID",
"description": "PID of the process that log messages are coming from (Default process.pid).",
"default": "process.pid"
},
"localhost": {
"type": "string",
"title": "Localhost",
"description": "Host to indicate that log messages are coming from (Default: localhost).",
"default": "localhost"
},
"type": {
"type": "string",
"title": "Type",
"description": "The type of the syslog protocol to use (Default: BSD, also valid: 5424).",
"default": "5424"
},
"app_name": {
"type": "string",
"title": "Localhost",
"description": "The name of the application (Default: Zigbee2MQTT).",
"default": "Zigbee2MQTT"
},
"eol": {
"type": "string",
"title": "eol",
"description": "The end of line character to be added to the end of the message (Default: Message without modifications).",
"default": "/n"
}
}
}
}
]
},
"pan_id": {
"oneOf": [{
+21 -8
View File
@@ -26,6 +26,10 @@ objectAssignDeep(schema, schemaJson);
delete schemaJson.properties.ban;
}
/** NOTE: by order of priority, lower index is lower level (more important) */
export const LOG_LEVELS: readonly string[] = ['error', 'warning', 'info', 'debug'] as const;
export type LogLevel = typeof LOG_LEVELS[number];
// DEPRECATED ZIGBEE2MQTT_CONFIG: https://github.com/Koenkk/zigbee2mqtt/issues/4697
const file = process.env.ZIGBEE2MQTT_CONFIG ?? data.joinPath('configuration.yaml');
const ajvSetting = new Ajv({allErrors: true}).addKeyword('requiresRestart').compile(schemaJson);
@@ -80,9 +84,12 @@ const defaults: RecursivePartial<Settings> = {
log_symlink_current: false,
log_output: ['console', 'file'],
log_directory: path.join(data.getPath(), 'log', '%TIMESTAMP%'),
log_file: 'log.txt',
log_file: 'log.log',
log_level: /* istanbul ignore next */ process.env.DEBUG ? 'debug' : 'info',
log_namespaced_levels: {},
log_syslog: {},
log_debug_to_mqtt_frontend: false,
log_debug_namespace_ignore: '',
pan_id: 0x1a62,
ext_pan_id: [0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD],
channel: 11,
@@ -184,6 +191,10 @@ function loadSettingsWithDefaults(): void {
_settingsWithDefaults.advanced.output = _settings.experimental.output;
}
if (_settings.advanced?.log_level === 'warn') {
_settingsWithDefaults.advanced.log_level = 'warning';
}
// @ts-ignore
_settingsWithDefaults.ban && _settingsWithDefaults.blocklist.push(..._settingsWithDefaults.ban);
// @ts-ignore
@@ -469,20 +480,22 @@ export function set(path: string[], value: string | number | boolean | KeyValue)
write();
}
export function apply(newSettings: Record<string, unknown>): boolean {
export function apply(settings: Record<string, unknown>): boolean {
getInternalSettings(); // Ensure _settings is initialized.
/* eslint-disable-line */ // @ts-ignore
const newSettings = objectAssignDeep.noMutate(_settings, settings);
utils.removeNullPropertiesFromObject(newSettings);
ajvSetting(newSettings);
const errors = ajvSetting.errors && ajvSetting.errors.filter((e) => e.keyword !== 'required');
if (errors.length) {
if (errors?.length) {
const error = errors[0];
throw new Error(`${error.instancePath.substring(1)} ${error.message}`);
}
getInternalSettings(); // Ensure _settings is initialized.
/* eslint-disable-line */ // @ts-ignore
_settings = objectAssignDeep.noMutate(_settings, newSettings);
_settings = newSettings;
write();
ajvRestartRequired(newSettings);
ajvRestartRequired(settings);
const restartRequired = ajvRestartRequired.errors &&
!!ajvRestartRequired.errors.find((e) => e.keyword === 'requiresRestart');
return restartRequired;
@@ -593,7 +606,7 @@ export function removeDevice(IDorName: string): void {
// Remove device from groups
if (settings.groups) {
const regex =
new RegExp(`^(${device.friendly_name}|${device.ID})(/(\\d|${utils.endpointNames.join('|')}))?$`);
new RegExp(`^(${device.friendly_name}|${device.ID})(/[^/]+)?$`);
for (const group of Object.values(settings.groups).filter((g) => g.devices)) {
group.devices = group.devices.filter((device) => !device.match(regex));
}
+3 -70
View File
@@ -4,8 +4,6 @@ import data from './data';
import vm from 'vm';
import fs from 'fs';
import path from 'path';
import {detailedDiff} from 'deep-object-diff';
import objectAssignDeep from 'object-assign-deep';
import type * as zhc from 'zigbee-herdsman-converters';
// construct a local ISO8601 string (instead of UTC-based)
@@ -30,22 +28,6 @@ function toLocalISOString(date: Date): string {
':' + pad(tzOffset % 60);
}
const endpointNames = [
'left', 'right', 'center', 'bottom_left', 'bottom_right', 'default',
'top_left', 'top_right', 'white', 'rgb', 'cct', 'system', 'top', 'bottom', 'center_left', 'center_right',
'ep1', 'ep2', 'row_1', 'row_2', 'row_3', 'row_4', 'relay', 'usb',
'l1', 'l2', 'l3', 'l4', 'l5', 'l6', 'l7', 'l8',
'l9', 'l10', 'l11', 'l12', 'l13', 'l14', 'l15', 'l16',
'l17', 'l18', 'l19', 'l20', 'l21', 'l22', 'l23', 'l24',
'th1', 'th2', 'th3', 'th4', 'th5', 'th6', 'th7', 'th8', 'th9', 'th10',
'button_1', 'button_2', 'button_3', 'button_4', 'button_5',
'button_6', 'button_7', 'button_8', 'button_9', 'button_10',
'button_11', 'button_12', 'button_13', 'button_14', 'button_15',
'button_16', 'button_17', 'button_18', 'button_19', 'button_20',
'button_light', 'button_fan_high', 'button_fan_med', 'button_fan_low',
'heat', 'cool', 'water', 'meter', 'wifi', 'no_occupancy_since',
];
function capitalize(s: string): string {
return s[0].toUpperCase() + s.slice(1);
}
@@ -252,16 +234,10 @@ function getAllFiles(path_: string): string[] {
function validateFriendlyName(name: string, throwFirstError=false): string[] {
const errors = [];
for (const endpointName of endpointNames) {
if (name.toLowerCase().endsWith('/' + endpointName)) {
errors.push(`friendly_name is not allowed to end with: '/${endpointName}'`);
}
}
if (name.length === 0) errors.push(`friendly_name must be at least 1 char long`);
if (name.endsWith('/') || name.startsWith('/')) errors.push(`friendly_name is not allowed to end or start with /`);
if (containsControlCharacter(name)) errors.push(`friendly_name is not allowed to contain control char`);
if (endpointNames.includes(name)) errors.push(`Following friendly_name are not allowed: '${endpointNames}'`);
if (name.match(/.*\/\d*$/)) errors.push(`Friendly name cannot end with a "/DIGIT" ('${name}')`);
if (name.includes('#') || name.includes('+')) {
errors.push(`MQTT wildcard (+ and #) not allowed in friendly_name ('${name}')`);
@@ -309,12 +285,6 @@ function isAvailabilityEnabledForEntity(entity: Device | Group, settings: Settin
return !blocklist.includes(entity.name) && !blocklist.includes(entity.ieeeAddr);
}
const entityIDRegex = new RegExp(`^(.+?)(?:/(${endpointNames.join('|')}|\\d+))?$`);
function parseEntityID(ID: string): {ID: string, endpoint: string} {
const match = ID.match(entityIDRegex);
return match && {ID: match[1], endpoint: match[2]};
}
function isEndpoint(obj: unknown): obj is zh.Endpoint {
return obj.constructor.name.toLowerCase() === 'endpoint';
}
@@ -364,10 +334,6 @@ function filterProperties(filter: string[], data: KeyValue): void {
}
}
function clone(obj: KeyValue): KeyValue {
return JSON.parse(JSON.stringify(obj));
}
export function isNumericExposeFeature(feature: zhc.Feature): feature is zhc.Numeric {
return feature?.type === 'numeric';
}
@@ -380,39 +346,6 @@ export function isBinaryExposeFeature(feature: zhc.Feature): feature is zhc.Bina
return feature?.type === 'binary';
}
function computeSettingsToChange(current: KeyValue, new_: KeyValue): KeyValue {
const diff: KeyValue = detailedDiff(current, new_);
// Remove any settings that are in the deleted.diff but not in the passed options
const cleanupDeleted = (options: KeyValue, deleted: KeyValue): void => {
for (const key of Object.keys(deleted)) {
if (!(key in options)) {
delete deleted[key];
} else if (!Array.isArray(options[key])) {
cleanupDeleted(options[key], deleted[key]);
}
}
};
cleanupDeleted(new_, diff.deleted);
// objectAssignDeep requires object prototype which is missing from detailedDiff, therefore clone
const newSettings = objectAssignDeep({}, clone(diff.added), clone(diff.updated), clone(diff.deleted));
// deep-object-diff converts arrays to objects, set original array back here
const convertBackArray = (before: KeyValue, after: KeyValue): void => {
for (const [key, afterValue] of Object.entries(after)) {
const beforeValue = before[key];
if (Array.isArray(beforeValue)) {
after[key] = beforeValue;
} else if (afterValue && typeof beforeValue === 'object') {
convertBackArray(beforeValue, afterValue);
}
}
};
convertBackArray(new_, newSettings);
return newSettings;
}
function getScenes(entity: zh.Endpoint | zh.Group): Scene[] {
const scenes: {[id: number]: Scene} = {};
const endpoints = isEndpoint(entity) ? [entity] : entity.members;
@@ -433,10 +366,10 @@ function getScenes(entity: zh.Endpoint | zh.Group): Scene[] {
}
export default {
endpointNames, capitalize, getZigbee2MQTTVersion, getDependencyVersion, formatDate, objectHasProperties,
capitalize, getZigbee2MQTTVersion, getDependencyVersion, formatDate, objectHasProperties,
equalsPartial, getObjectProperty, getResponse, parseJSON, loadModuleFromText, loadModuleFromFile,
removeNullPropertiesFromObject, toNetworkAddressHex, toSnakeCase,
parseEntityID, isEndpoint, isZHGroup, hours, minutes, seconds, validateFriendlyName, sleep,
isEndpoint, isZHGroup, hours, minutes, seconds, validateFriendlyName, sleep,
sanitizeImageParameter, isAvailabilityEnabledForEntity, publishLastSeen, availabilityPayload,
getAllFiles, filterProperties, flatten, arrayUnique, clone, computeSettingsToChange, getScenes,
getAllFiles, filterProperties, flatten, arrayUnique, getScenes,
};
+41 -7
View File
@@ -11,6 +11,8 @@ import * as ZHEvents from 'zigbee-herdsman/dist/controller/events';
import bind from 'bind-decorator';
import {randomInt} from 'crypto';
const entityIDRegex = new RegExp(`^(.+?)(?:/([^/]+))?$`);
export default class Zigbee {
private herdsman: Controller;
private eventBus: EventBus;
@@ -56,7 +58,7 @@ export default class Zigbee {
let startResult;
try {
this.herdsman = new Controller(herdsmanSettings, logger);
this.herdsman = new Controller(herdsmanSettings);
startResult = await this.herdsman.start();
} catch (error) {
logger.error(`Error while starting zigbee-herdsman`);
@@ -86,22 +88,22 @@ export default class Zigbee {
});
this.herdsman.on('deviceInterview', async (data: ZHEvents.DeviceInterviewPayload) => {
const device = this.resolveDevice(data.device.ieeeAddr);
await device.resolveDefinition();
/* istanbul ignore if */ if (!device) return; // Prevent potential race
await device.resolveDefinition();
const d = {device, status: data.status};
this.logDeviceInterview(d);
this.eventBus.emitDeviceInterview(d);
});
this.herdsman.on('deviceJoined', async (data: ZHEvents.DeviceJoinedPayload) => {
const device = this.resolveDevice(data.device.ieeeAddr);
await device.resolveDefinition();
/* istanbul ignore if */ if (!device) return; // Prevent potential race
await device.resolveDefinition();
logger.info(`Device '${device.name}' joined`);
this.eventBus.emitDeviceJoined({device});
});
this.herdsman.on('deviceLeave', (data: ZHEvents.DeviceLeavePayload) => {
const name = settings.getDevice(data.ieeeAddr)?.friendly_name || data.ieeeAddr;
logger.warn(`Device '${name}' left the network`);
logger.warning(`Device '${name}' left the network`);
this.eventBus.emitDeviceLeave({ieeeAddr: data.ieeeAddr, name});
});
this.herdsman.on('message', async (data: ZHEvents.MessagePayload) => {
@@ -132,11 +134,11 @@ export default class Zigbee {
};
if (passlist.length > 0) {
if (!passlist.includes(device.ieeeAddr)) {
logger.warn(`Device which is not on passlist connected (${device.ieeeAddr}), removing...`);
logger.warning(`Device which is not on passlist connected (${device.ieeeAddr}), removing...`);
await remove(device);
}
} else if (blocklist.includes(device.ieeeAddr)) {
logger.warn(`Device on blocklist is connected (${device.ieeeAddr}), removing...`);
logger.warning(`Device on blocklist is connected (${device.ieeeAddr}), removing...`);
await remove(device);
}
}
@@ -160,7 +162,7 @@ export default class Zigbee {
const {vendor, description, model} = data.device.definition;
logger.info(`Device '${name}' is supported, identified as: ${vendor} ${description} (${model})`);
} else {
logger.warn(`Device '${name}' with Zigbee model '${data.device.zh.modelID}' and manufacturer name ` +
logger.warning(`Device '${name}' with Zigbee model '${data.device.zh.modelID}' and manufacturer name ` +
`'${data.device.zh.manufacturerName}' is NOT supported, ` +
// eslint-disable-next-line max-len
`please follow https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html`);
@@ -283,6 +285,38 @@ export default class Zigbee {
}
}
resolveEntityAndEndpoint(ID: string)
: {ID: string, entity: Device | Group, endpointID: string, endpoint: zh.Endpoint} {
// This function matches the following entity formats:
// device_name (just device name)
// device_name/ep_name (device name and endpoint numeric ID or name)
// device/name (device name with slashes)
// device/name/ep_name (device name with slashes, and endpoint numeric ID or name)
// The function tries to find an exact match first
let entityName = ID;
let deviceOrGroup = this.resolveEntity(ID);
let endpointNameOrID = undefined;
// If exact match did not happenc, try matching a device_name/endpoint pattern
if (!deviceOrGroup) {
// First split the input token by the latest slash
const match = ID.match(entityIDRegex);
// Get the resulting IDs from the match
entityName = match[1];
deviceOrGroup = this.resolveEntity(match[1]);
endpointNameOrID = match[2];
}
// If the function returns non-null endpoint name, but the endpoint field is null, then
// it means that endpoint was not matched because there is no such endpoint on the device
// (or the entity is a group)
const endpoint = deviceOrGroup?.isDevice() ? deviceOrGroup.endpoint(endpointNameOrID) : null;
return {ID: entityName, entity: deviceOrGroup, endpointID: endpointNameOrID, endpoint: endpoint};
}
firstCoordinatorEndpoint(): zh.Endpoint {
return this.herdsman.getDevicesByType('Coordinator')[0].endpoints[0];
}
+963 -1136
View File
File diff suppressed because it is too large Load Diff
+29 -31
View File
@@ -1,6 +1,6 @@
{
"name": "zigbee2mqtt",
"version": "1.35.2",
"version": "1.38.0",
"description": "Zigbee to MQTT bridge using Zigbee-herdsman",
"main": "index.js",
"repository": {
@@ -8,7 +8,7 @@
"url": "git+https://github.com/Koenkk/zigbee2mqtt.git"
},
"engines": {
"node": "^18 || ^20 || ^21"
"node": "^18 || ^20 || ^22"
},
"keywords": [
"xiaomi",
@@ -24,9 +24,9 @@
"build-watch": "tsc --watch",
"eslint": "eslint lib/ --max-warnings=0",
"start": "node index.js",
"test-with-coverage": "jest test --coverage",
"test": "jest test",
"test-watch": "jest test --watch"
"test-with-coverage": "jest test --silent --maxWorkers=50% --coverage",
"test": "jest test --silent --maxWorkers=50%",
"test-watch": "jest test --silent --maxWorkers=25% --watch"
},
"author": "Koen Kanters",
"license": "GPL-3.0",
@@ -35,58 +35,56 @@
},
"homepage": "https://koenkk.github.io/zigbee2mqtt",
"dependencies": {
"ajv": "^8.12.0",
"ajv": "^8.13.0",
"bind-decorator": "^1.0.11",
"connect-gzip-static": "3.0.1",
"debounce": "^2.0.0",
"deep-object-diff": "^1.1.9",
"fast-deep-equal": "^3.1.3",
"finalhandler": "^1.2.0",
"git-last-commit": "^1.0.1",
"humanize-duration": "^3.31.0",
"humanize-duration": "^3.32.0",
"js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"jszip": "^3.10.1",
"mkdir-recursive": "^0.4.0",
"moment": "^2.30.1",
"mqtt": "^5.3.4",
"mqtt": "^5.5.0",
"object-assign-deep": "^0.4.0",
"rimraf": "^5.0.5",
"semver": "^7.5.4",
"semver": "^7.6.0",
"source-map-support": "^0.5.21",
"uri-js": "^4.4.1",
"winston": "^3.11.0",
"winston": "^3.13.0",
"winston-syslog": "^2.7.0",
"winston-transport": "^4.6.0",
"ws": "^8.16.0",
"zigbee-herdsman": "0.33.5",
"zigbee-herdsman-converters": "18.25.1",
"zigbee2mqtt-frontend": "0.6.152"
"winston-transport": "^4.7.0",
"ws": "^8.17.0",
"zigbee-herdsman": "0.49.2",
"zigbee-herdsman-converters": "19.47.1",
"zigbee2mqtt-frontend": "0.7.1"
},
"devDependencies": {
"@babel/core": "^7.23.7",
"@babel/plugin-proposal-decorators": "^7.23.7",
"@babel/preset-env": "^7.23.7",
"@babel/preset-typescript": "^7.23.3",
"@babel/core": "^7.24.5",
"@babel/plugin-proposal-decorators": "^7.24.1",
"@babel/preset-env": "^7.24.5",
"@babel/preset-typescript": "^7.24.1",
"@types/debounce": "^1.2.4",
"@types/finalhandler": "^1.2.3",
"@types/humanize-duration": "^3.27.3",
"@types/jest": "^29.5.11",
"@types/humanize-duration": "^3.27.4",
"@types/jest": "^29.5.12",
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.10.6",
"@types/node": "^20.12.8",
"@types/object-assign-deep": "^0.4.3",
"@types/readable-stream": "4.0.10",
"@types/rimraf": "^3.0.2",
"@types/readable-stream": "4.0.12",
"@types/ws": "8.5.10",
"@typescript-eslint/eslint-plugin": "^6.16.0",
"@typescript-eslint/parser": "^6.16.0",
"@typescript-eslint/eslint-plugin": "^7.8.0",
"@typescript-eslint/parser": "^7.8.0",
"babel-jest": "^29.7.0",
"eslint": "^8.56.0",
"eslint": "^8.57.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-jest": "^27.6.0",
"eslint-plugin-jest": "^28.5.0",
"jest": "^29.7.0",
"tmp": "^0.2.1",
"typescript": "^5.3.3"
"tmp": "^0.2.3",
"typescript": "^5.4.5"
},
"overrides": {
"zigbee-herdsman-converters": {
+1
View File
@@ -7,5 +7,6 @@
"draft": true
}
},
"pull-request-title-pattern": "chore${scope}: release ${version}",
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
}
+1 -1
View File
@@ -100,7 +100,7 @@ for (const changelog of changelogs) {
} else if (line === '# Changelog' || line === '### ⚠ BREAKING CHANGES' || !line) {
continue;
} else {
throw new Error(`Unmatched line: ${line}`);
changes['error'].push(`- Unmatched line: ${line}`);
}
}
}
+1 -1
View File
@@ -10,7 +10,7 @@ import Availability from '../lib/extension/availability';
import flushPromises from './lib/flushPromises';
import stringify from 'json-stable-stringify-without-jsonify';
const mocks = [MQTT.publish, logger.warn, logger.debug];
const mocks = [MQTT.publish, logger.warning, logger.debug];
const devices = zigbeeHerdsman.devices;
zigbeeHerdsman.returnDevices.push(
...[devices.bulb_color.ieeeAddr, devices.bulb_color_2.ieeeAddr, devices.coordinator.ieeeAddr, devices.remote.ieeeAddr])
+31 -3
View File
@@ -157,7 +157,7 @@ describe('Bind', () => {
expect(target.configureReporting).toHaveBeenCalledWith("genOnOff",[{"attribute": "onOff", "maximumReportInterval": 0xFFFF, "minimumReportInterval": 0, "reportableChange": 0}]);
expect(target.configureReporting).toHaveBeenCalledWith("genLevelCtrl",[{"attribute": "currentLevel", "maximumReportInterval": 0xFFFF, "minimumReportInterval": 5, "reportableChange": 1}]);
expect(target.configureReporting).toHaveBeenCalledWith("lightingColorCtrl",[{"attribute":"colorTemperature","minimumReportInterval":5,"maximumReportInterval":0xFFFF,"reportableChange":1},{"attribute":"currentX","minimumReportInterval":5,"maximumReportInterval":0xFFFF,"reportableChange":1},{"attribute":"currentY","minimumReportInterval":5,"maximumReportInterval":0xFFFF,"reportableChange":1}]);
expect(zigbeeHerdsman.devices.bulb_color.meta.configured).toBe(1324213189);
expect(zigbeeHerdsman.devices.bulb_color.meta.configured).toBe(332242049);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/unbind',
stringify({"data":{"from":"remote","to":"bulb_color","clusters":["genScenes","genOnOff","genLevelCtrl"],"failed":[]},"status":"ok"}),
@@ -387,7 +387,7 @@ describe('Bind', () => {
);
});
it('Error bind fails when source not existing', async () => {
it('Error bind fails when source device does not exist', async () => {
const device = zigbeeHerdsman.devices.remote;
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
const endpoint = device.getEndpoint(1);
@@ -401,7 +401,21 @@ describe('Bind', () => {
);
});
it('Error bind fails when target not existing', async () => {
it("Error bind fails when source device's endpoint does not exist", async () => {
const device = zigbeeHerdsman.devices.remote;
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
const endpoint = device.getEndpoint(1);
mockClear(device);
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote/not_existing_endpoint', to: 'bulb_color'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/bind',
stringify({"data":{"from":"remote/not_existing_endpoint","to":"bulb_color"},"status":"error","error":"Source device 'remote' does not have endpoint 'not_existing_endpoint'"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Error bind fails when target device or group does not exist', async () => {
const device = zigbeeHerdsman.devices.remote;
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
const endpoint = device.getEndpoint(1);
@@ -415,6 +429,20 @@ describe('Bind', () => {
);
});
it("Error bind fails when target device's endpoint does not exist", async () => {
const device = zigbeeHerdsman.devices.remote;
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
const endpoint = device.getEndpoint(1);
mockClear(device);
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote', to: 'bulb_color/not_existing_endpoint'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/bind',
stringify({"data":{"from":"remote","to":"bulb_color/not_existing_endpoint"},"status":"error","error":"Target device 'bulb_color' does not have endpoint 'not_existing_endpoint'"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Legacy api: Should bind', async () => {
const device = zigbeeHerdsman.devices.remote;
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
+322 -18
View File
File diff suppressed because one or more lines are too long
+6 -6
View File
@@ -8,7 +8,7 @@ const flushPromises = require('./lib/flushPromises');
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const stringify = require('json-stable-stringify-without-jsonify');
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
const mocksClear = [MQTT.publish, logger.warning, logger.debug];
describe('Configure', () => {
let controller;
@@ -24,7 +24,7 @@ describe('Configure', () => {
const endpoint2 = device.getEndpoint(2);
expect(endpoint2.write).toHaveBeenCalledTimes(1);
expect(endpoint2.write).toHaveBeenCalledWith("genBasic", {"49": {"type": 25, "value": 11}}, {"disableDefaultResponse": true, "manufacturerCode": 4107});
expect(device.meta.configured).toBe(1);
expect(device.meta.configured).toBe(332242049);
}
const expectBulbConfigured = () => {
@@ -176,11 +176,11 @@ describe('Configure', () => {
});
it('Fail to configure via MQTT when device has no configure', async () => {
await MQTT.events.message('zigbee2mqtt/bridge/request/device/configure', stringify({id: "0x0017880104e45521", transaction: 20}));
await MQTT.events.message('zigbee2mqtt/bridge/request/device/configure', stringify({id: "0x0017882104a44559", transaction: 20}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/configure',
stringify({"data":{"id": "0x0017880104e45521"},"status":"error","error": "Device 'button_double_key' cannot be configured","transaction":20}),
stringify({"data":{"id": "0x0017882104a44559"},"status":"error","error": "Device 'TS0601_thermostat' cannot be configured","transaction":20}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -200,9 +200,9 @@ describe('Configure', () => {
});
it('Legacy api: Should skip reconfigure when device does not require this', async () => {
await MQTT.events.message('zigbee2mqtt/bridge/configure', '0x0017880104e45521');
await MQTT.events.message('zigbee2mqtt/bridge/configure', '0x0017882104a44559');
await flushPromises();
expect(logger.warn).toHaveBeenCalledWith(`Skipping configure of 'button_double_key', device does not require this.`)
expect(logger.warning).toHaveBeenCalledWith(`Skipping configure of 'TS0601_thermostat', device does not require this.`)
});
it('Should not configure when interview not completed', async () => {
+12 -10
View File
@@ -17,6 +17,8 @@ const mocksClear = [
const fs = require('fs');
const LOG_MQTT_NS = 'z2m:mqtt';
jest.mock('sd-notify', () => {
return {
watchdogInterval: () => {return 3000;},
@@ -53,13 +55,13 @@ describe('Controller', () => {
it('Start controller', async () => {
await controller.start();
expect(zigbeeHerdsman.constructor).toHaveBeenCalledWith({"network":{"panID":6754,"extendedPanID":[221,221,221,221,221,221,221,221],"channelList":[11],"networkKey":[1,3,5,7,9,11,13,15,0,2,4,6,8,10,12,13]},"databasePath":path.join(data.mockDir, "database.db"), "databaseBackupPath":path.join(data.mockDir, "database.db.backup"),"backupPath":path.join(data.mockDir, "coordinator_backup.json"),"acceptJoiningDeviceHandler": expect.any(Function),adapter: {concurrent: null, delay: null, disableLED: false}, "serialPort":{"baudRate":undefined,"rtscts":undefined,"path":"/dev/dummy"}}, logger);
expect(zigbeeHerdsman.constructor).toHaveBeenCalledWith({"network":{"panID":6754,"extendedPanID":[221,221,221,221,221,221,221,221],"channelList":[11],"networkKey":[1,3,5,7,9,11,13,15,0,2,4,6,8,10,12,13]},"databasePath":path.join(data.mockDir, "database.db"), "databaseBackupPath":path.join(data.mockDir, "database.db.backup"),"backupPath":path.join(data.mockDir, "coordinator_backup.json"),"acceptJoiningDeviceHandler": expect.any(Function),adapter: {concurrent: null, delay: null, disableLED: false}, "serialPort":{"baudRate":undefined,"rtscts":undefined,"path":"/dev/dummy"}});
expect(zigbeeHerdsman.start).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.setTransmitPower).toHaveBeenCalledTimes(0);
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledWith(true, undefined, undefined);
expect(logger.info).toHaveBeenCalledWith(`Currently ${Object.values(zigbeeHerdsman.devices).length - 1} devices are joined:`)
expect(logger.info).toHaveBeenCalledWith('bulb (0x000b57fffec6a5b2): LED1545G12 - IKEA TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white (Router)');
expect(logger.info).toHaveBeenCalledWith('bulb (0x000b57fffec6a5b2): LED1545G12 - IKEA TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm (Router)');
expect(logger.info).toHaveBeenCalledWith('remote (0x0017880104e45517): 324131092621 - Philips Hue dimmer switch (EndDevice)');
expect(logger.info).toHaveBeenCalledWith('0x0017880104e45518 (0x0017880104e45518): Not supported (EndDevice)');
expect(MQTT.connect).toHaveBeenCalledTimes(1);
@@ -161,7 +163,7 @@ describe('Controller', () => {
logger.error.mockClear();
controller.mqtt.client.reconnecting = true;
jest.advanceTimersByTime(11 * 1000);
expect(logger.error).toHaveBeenCalledWith("Not connected to MQTT server!");
expect(logger.error).toHaveBeenCalledWith("Not connected to MQTT server!", LOG_MQTT_NS);
controller.mqtt.client.reconnecting = false;
});
@@ -174,8 +176,8 @@ describe('Controller', () => {
await controller.publishEntityState(device, {state: 'ON', brightness: 50, color_temp: 370, color: {r: 100, g: 50, b: 10}, dummy: {1: 'yes', 2: 'no'}});
await flushPromises();
expect(logger.error).toHaveBeenCalledTimes(2);
expect(logger.error).toHaveBeenCalledWith("Not connected to MQTT server!");
expect(logger.error).toHaveBeenCalledWith("Cannot send message: topic: 'zigbee2mqtt/bulb', payload: '{\"brightness\":50,\"color\":{\"b\":10,\"g\":50,\"r\":100},\"color_temp\":370,\"dummy\":{\"1\":\"yes\",\"2\":\"no\"},\"linkquality\":99,\"state\":\"ON\"}");
expect(logger.error).toHaveBeenCalledWith("Not connected to MQTT server!", LOG_MQTT_NS);
expect(logger.error).toHaveBeenCalledWith("Cannot send message: topic: 'zigbee2mqtt/bulb', payload: '{\"brightness\":50,\"color\":{\"b\":10,\"g\":50,\"r\":100},\"color_temp\":370,\"dummy\":{\"1\":\"yes\",\"2\":\"no\"},\"linkquality\":99,\"state\":\"ON\"}", LOG_MQTT_NS);
controller.mqtt.client.reconnecting = false;
});
@@ -215,7 +217,7 @@ describe('Controller', () => {
});
await controller.start();
await flushPromises();
expect(logger.error).toHaveBeenCalledWith('MQTT error: addr not found');
expect(logger.error).toHaveBeenCalledWith('MQTT error: addr not found', LOG_MQTT_NS);
expect(logger.error).toHaveBeenCalledWith('MQTT failed to connect, exiting...');
expect(mockExit).toHaveBeenCalledTimes(1);
expect(mockExit).toHaveBeenCalledWith(1, false);
@@ -269,14 +271,14 @@ describe('Controller', () => {
await controller.start();
logger.debug.mockClear();
await MQTT.events.message('dummytopic', 'dummymessage');
expect(logger.debug).toHaveBeenCalledWith("Received MQTT message on 'dummytopic' with data 'dummymessage'")
expect(logger.debug).toHaveBeenCalledWith("Received MQTT message on 'dummytopic' with data 'dummymessage'", LOG_MQTT_NS)
});
it('Skip MQTT messages on topic we published to', async () => {
await controller.start();
logger.debug.mockClear();
await MQTT.events.message('zigbee2mqtt/skip-this-topic', 'skipped');
expect(logger.debug).toHaveBeenCalledWith("Received MQTT message on 'zigbee2mqtt/skip-this-topic' with data 'skipped'")
expect(logger.debug).toHaveBeenCalledWith("Received MQTT message on 'zigbee2mqtt/skip-this-topic' with data 'skipped'", LOG_MQTT_NS)
logger.debug.mockClear();
await controller.mqtt.publish('skip-this-topic', '', {});
await MQTT.events.message('zigbee2mqtt/skip-this-topic', 'skipped');
@@ -398,7 +400,7 @@ describe('Controller', () => {
const payload = {device, status: 'successful'};
await zigbeeHerdsman.events.deviceInterview(payload);
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', stringify({"type":"pairing","message":"interview_successful","meta":{"friendly_name":"bulb","model":"LED1545G12","vendor":"IKEA","description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","supported":true}}), { retain: false, qos: 0 }, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', stringify({"type":"pairing","message":"interview_successful","meta":{"friendly_name":"bulb","model":"LED1545G12","vendor":"IKEA","description":"TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm","supported":true}}), { retain: false, qos: 0 }, expect.any(Function));
});
it('On zigbee deviceInterview successful not supported', async () => {
@@ -665,7 +667,7 @@ describe('Controller', () => {
MQTT.events['connect']();
await flushPromises();
jest.runOnlyPendingTimers();
expect(MQTT.publish).toHaveBeenCalledTimes(13);
expect(MQTT.publish).toHaveBeenCalledTimes(14);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), { retain: true, qos: 0 }, expect.any(Function));
});
+24 -2
View File
@@ -273,7 +273,7 @@ describe('Groups', () => {
expect(logger.error).toHaveBeenCalledWith("Group 'group_1_not_existing' does not exist");
});
it('Legacy api: Log when adding to non-existing device', async () => {
it('Legacy api: Log when adding a non-existing device', async () => {
await resetExtension();
logger.error.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/group/group_1/add', 'bulb_color_not_existing');
@@ -281,6 +281,14 @@ describe('Groups', () => {
expect(logger.error).toHaveBeenCalledWith("Device 'bulb_color_not_existing' does not exist");
});
it('Legacy api: Log when adding a non-existing endpoint', async () => {
await resetExtension();
logger.error.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/group/group_1/add', 'bulb_color/not_existing_endpoint');
await flushPromises();
expect(logger.error).toHaveBeenCalledWith("Device 'bulb_color' does not have endpoint 'not_existing_endpoint'");
});
it('Should publish group state change when a device in it changes state', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
@@ -869,7 +877,7 @@ describe('Groups', () => {
);
});
it('Error when adding to non-existing device', async () => {
it('Error when adding a non-existing device', async () => {
await resetExtension();
logger.error.mockClear();
MQTT.publish.mockClear();
@@ -883,6 +891,20 @@ describe('Groups', () => {
);
});
it('Error when adding a non-existing endpoint', async () => {
await resetExtension();
logger.error.mockClear();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', device: 'bulb_color/not_existing_endpoint'}));
await flushPromises();
expect(MQTT.publish).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/members/add',
stringify({"data":{"device":"bulb_color/not_existing_endpoint","group":"group_1"},"status":"error","error":"Device 'bulb_color' does not have endpoint 'not_existing_endpoint'"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Should only include relevant properties when publishing member states', async () => {
const bulbColor = zigbeeHerdsman.devices.bulb_color;
const bulbColorTemp = zigbeeHerdsman.devices.bulb;
+95 -22
View File
@@ -15,11 +15,23 @@ describe('HomeAssistant extension', () => {
let extension;
let origin;
let resetExtension = async () => {
let resetExtension = async (runTimers=true) => {
await controller.enableDisableExtension(false, 'HomeAssistant');
MQTT.publish.mockClear();
await controller.enableDisableExtension(true, 'HomeAssistant');
extension = controller.extensions.find((e) => e.constructor.name === 'HomeAssistant');
if (runTimers) {
jest.runOnlyPendingTimers();
}
}
let resetDiscoveryPayloads = (id) => {
// Change discovered payload, otherwise it's not re-published because it's the same.
Object.values(extension.discovered[id].messages).forEach((m) => m.payload = 'changed');
}
let clearDiscoveredTrigger = (id) => {
extension.discovered[id].triggers = new Set();
}
beforeEach(async () => {
@@ -29,6 +41,7 @@ describe('HomeAssistant extension', () => {
data.writeEmptyState();
controller.state.load();
await resetExtension();
await flushPromises();
});
beforeAll(async () => {
@@ -77,13 +90,11 @@ describe('HomeAssistant extension', () => {
it('Should discover devices and groups', async () => {
let payload;
await flushPromises();
payload = {
"availability":[{"topic":"zigbee2mqtt/bridge/state"}],
"brightness":true,
"brightness_scale":254,
"color_mode":true,
"command_topic":"zigbee2mqtt/ha_discovery_group/set",
"device":{
"identifiers":["zigbee2mqtt_1221051039810110150109113116116_9"],
@@ -371,7 +382,6 @@ describe('HomeAssistant extension', () => {
"availability":[{"topic":"zigbee2mqtt/bridge/state"}],
"brightness":true,
"brightness_scale":254,
"color_mode": true,
"supported_color_modes": ["color_temp"],
"min_mireds": 250,
"max_mireds": 454,
@@ -381,7 +391,7 @@ describe('HomeAssistant extension', () => {
"zigbee2mqtt_0x000b57fffec6a5b2"
],
"manufacturer":"IKEA",
"model":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white (LED1545G12)",
"model":"TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm (LED1545G12)",
"name":"bulb",
"sw_version": null,
"via_device": "zigbee2mqtt_bridge_0x00124b00120144ae",
@@ -412,6 +422,68 @@ describe('HomeAssistant extension', () => {
);
});
it('Should not discovery devices which are already discovered', async() => {
await resetExtension(false);
const topic1 = 'homeassistant/sensor/0x0017880104e45522/humidity/config';
const payload1 = stringify({
'unit_of_measurement': '%',
'device_class': 'humidity',
'state_class': 'measurement',
'value_template': '{{ value_json.humidity }}',
'state_topic': 'zigbee2mqtt/weather_sensor',
'json_attributes_topic': 'zigbee2mqtt/weather_sensor',
'object_id': 'weather_sensor_humidity',
'unique_id': '0x0017880104e45522_humidity_zigbee2mqtt',
'origin': origin,
'enabled_by_default': true,
'device': {
'identifiers': ['zigbee2mqtt_0x0017880104e45522'],
'name': 'weather_sensor',
'sw_version': null,
'model': 'Temperature and humidity sensor (WSDCGQ11LM)',
'manufacturer': 'Aqara',
'via_device': 'zigbee2mqtt_bridge_0x00124b00120144ae',
},
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
});
const topic2 = 'homeassistant/device_automation/0x0017880104e45522/action_double/config';
const payload2 = stringify({
"automation_type":"trigger",
"type":"action",
"subtype":"double",
"payload":"double",
"topic":"zigbee2mqtt/weather_sensor_renamed/action",
'origin': origin,
"device":{
"identifiers":[
"zigbee2mqtt_0x0017880104e45522"
],
"name":"weather_sensor_renamed",
"sw_version": null,
"model":"Temperature and humidity sensor (WSDCGQ11LM)",
"manufacturer":"Aqara",
"via_device": "zigbee2mqtt_bridge_0x00124b00120144ae",
}
});
// Should subscribe to `homeassistant/#` to find out what devices are already discovered.
expect(MQTT.subscribe).toHaveBeenCalledWith(`homeassistant/#`);
// Retained Home Assistant discovery message arrives
await MQTT.events.message(topic1, payload1);
await MQTT.events.message(topic2, payload2);
jest.runOnlyPendingTimers();
// Should unsubscribe to not receive all messages that are going to be published to `homeassistant/#` again.
expect(MQTT.unsubscribe).toHaveBeenCalledWith(`homeassistant/#`);
expect(MQTT.publish).not.toHaveBeenCalledWith(topic1, expect.anything(), expect.any(Object), expect.any(Function));
// Device automation should not be cleared
expect(MQTT.publish).not.toHaveBeenCalledWith(topic2, null, expect.any(Object), expect.any(Function));
expect(logger.debug).toHaveBeenCalledWith(`Skipping discovery of 'sensor/0x0017880104e45522/humidity/config', already discovered`)
});
it('Should discover devices with precision', async () => {
settings.set(['devices', '0x0017880104e45522'], {
humidity_precision: 0,
@@ -993,9 +1065,9 @@ describe('HomeAssistant extension', () => {
it('Should warn when starting with cache_state false', async () => {
settings.set(['advanced', 'cache_state'], false);
logger.warn.mockClear();
logger.warning.mockClear();
await resetExtension();
expect(logger.warn).toHaveBeenCalledWith("In order for Home Assistant integration to work properly set `cache_state: true");
expect(logger.warning).toHaveBeenCalledWith("In order for Home Assistant integration to work properly set `cache_state: true");
});
it('Should set missing values to null', async () => {
@@ -1075,7 +1147,7 @@ describe('HomeAssistant extension', () => {
});
it('Should discover when not discovered yet', async () => {
controller.extensions.find((e) => e.constructor.name === 'HomeAssistant').discovered = {};
extension.discovered = {};
const device = zigbeeHerdsman.devices.WSDCGQ11LM;
const data = {measuredValue: -85}
const payload = {data, cluster: 'msTemperatureMeasurement', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
@@ -1113,7 +1185,7 @@ describe('HomeAssistant extension', () => {
});
it('Shouldnt discover when device leaves', async () => {
controller.extensions.find((e) => e.constructor.name === 'HomeAssistant').discovered = {};
extension.discovered = {};
const device = zigbeeHerdsman.devices.bulb;
const payload = {ieeeAddr: device.ieeeAddr};
MQTT.publish.mockClear();
@@ -1123,6 +1195,7 @@ describe('HomeAssistant extension', () => {
it('Should discover when options change', async () => {
const device = controller.zigbee.resolveEntity(zigbeeHerdsman.devices.bulb);
resetDiscoveryPayloads(device.ieeeAddr);
MQTT.publish.mockClear();
controller.eventBus.emitEntityOptionsChanged({entity: device, from: {}, to: {'test': 123}});
await flushPromises();
@@ -1369,7 +1442,6 @@ describe('HomeAssistant extension', () => {
"availability":[{"topic":"zigbee2mqtt/bridge/state"}],
"brightness":true,
"brightness_scale":254,
"color_mode":true,
"command_topic":"zigbee2mqtt/ha_discovery_group_new/set",
"device":{
"identifiers":["zigbee2mqtt_1221051039810110150109113116116_9"],
@@ -1482,7 +1554,7 @@ describe('HomeAssistant extension', () => {
],
"name":"bulb",
'sw_version': null,
"model":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white (LED1545G12)",
"model":"TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm (LED1545G12)",
"manufacturer":"IKEA",
"via_device": "zigbee2mqtt_bridge_0x00124b00120144ae",
},
@@ -1632,7 +1704,7 @@ describe('HomeAssistant extension', () => {
);
// Shouldn't rediscover when already discovered in previous session
controller.extensions.find((e) => e.constructor.name === 'HomeAssistant')._clearDiscoveredTrigger();
clearDiscoveredTrigger('0x0017880104e45520');
await MQTT.events.message('homeassistant/device_automation/0x0017880104e45520/action_double/config', stringify({topic: 'zigbee2mqtt/button/action'}));
await MQTT.events.message('homeassistant/device_automation/0x0017880104e45520/action_double/config', stringify({topic: 'zigbee2mqtt/button/action'}));
await flushPromises();
@@ -1643,7 +1715,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).not.toHaveBeenCalledWith('homeassistant/device_automation/0x0017880104e45520/action_double/config', expect.any(String), expect.any(Object), expect.any(Function));
// Should rediscover when already discovered in previous session but with different name
controller.extensions.find((e) => e.constructor.name === 'HomeAssistant')._clearDiscoveredTrigger();
clearDiscoveredTrigger('0x0017880104e45520');
await MQTT.events.message('homeassistant/device_automation/0x0017880104e45520/action_double/config', stringify({topic: 'zigbee2mqtt/button_other_name/action'}));
await flushPromises();
MQTT.publish.mockClear();
@@ -1908,6 +1980,7 @@ describe('HomeAssistant extension', () => {
});
it('Should rediscover group when device is added to it', async () => {
resetDiscoveryPayloads(9);
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'ha_discovery_group', device: 'wall_switch_double/left'}));
await flushPromises();
@@ -1916,7 +1989,6 @@ describe('HomeAssistant extension', () => {
"availability":[{"topic":"zigbee2mqtt/bridge/state"}],
"brightness":true,
"brightness_scale":254,
"color_mode":true,
"command_topic":"zigbee2mqtt/ha_discovery_group/set",
"device":{
"identifiers":["zigbee2mqtt_1221051039810110150109113116116_9"],
@@ -1970,7 +2042,6 @@ describe('HomeAssistant extension', () => {
"availability":[{"topic":"zigbee2mqtt/bridge/state","value_template":'{{ value_json.state }}'}],
"brightness":true,
"brightness_scale":254,
"color_mode":true,
"command_topic":"zigbee2mqtt/ha_discovery_group/set",
"device":{
"identifiers":["zigbee2mqtt_1221051039810110150109113116116_9"],
@@ -2030,14 +2101,13 @@ describe('HomeAssistant extension', () => {
],
"brightness":true,
"brightness_scale":254,
"color_mode":true,
"command_topic":"zigbee2mqtt/bulb/set",
"device":{
"identifiers":[
"zigbee2mqtt_0x000b57fffec6a5b2"
],
"manufacturer":"IKEA",
"model":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white (LED1545G12)",
"model":"TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm (LED1545G12)",
"name":"bulb",
"sw_version":null,
"via_device": "zigbee2mqtt_bridge_0x00124b00120144ae",
@@ -2088,7 +2158,7 @@ describe('HomeAssistant extension', () => {
"zigbee2mqtt_0x000b57fffec6a5b2"
],
"manufacturer":"IKEA",
"model":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white (LED1545G12)",
"model":"TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm (LED1545G12)",
"name":"bulb",
"sw_version": null,
"via_device": "zigbee2mqtt_bridge_0x00124b00120144ae",
@@ -2154,9 +2224,9 @@ describe('HomeAssistant extension', () => {
});
it('Should rediscover scenes when a scene is changed', async () => {
// Device/endpoint scenes.
const device = controller.zigbee.resolveEntity(zigbeeHerdsman.devices.bulb_color_2);
resetDiscoveryPayloads(device.ieeeAddr);
MQTT.publish.mockClear();
controller.eventBus.emitScenesChanged({entity: device});
@@ -2200,6 +2270,7 @@ describe('HomeAssistant extension', () => {
// Group scenes.
const group = controller.zigbee.resolveEntity('ha_discovery_group');
resetDiscoveryPayloads(9);
MQTT.publish.mockClear();
controller.eventBus.emitScenesChanged({entity: group});
@@ -2369,7 +2440,7 @@ describe('HomeAssistant extension', () => {
'command_topic': 'zigbee2mqtt/bridge/request/options',
'command_template':
'{"options": {"advanced": {"log_level": "{{ value }}" } } }',
'options': ['info', 'warn', 'error', 'debug'],
'options': settings.LOG_LEVELS,
'origin': origin,
'device': devicePayload,
'availability': [{'topic': 'zigbee2mqtt/bridge/state'}],
@@ -2508,7 +2579,8 @@ describe('HomeAssistant extension', () => {
MQTT.publish.mockClear();
const device = zigbeeHerdsman.devices['BMCT-SLZ'];
const data = {deviceMode: 0}
const msg = {data, cluster: 'manuSpecificBosch10', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
const msg = {data, cluster: 'boschSpecific', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
resetDiscoveryPayloads('0x18fc26000000cafe');
await zigbeeHerdsman.events.message(msg);
const payload = {
'availability':[{'topic':'zigbee2mqtt/bridge/state'}],
@@ -2530,7 +2602,8 @@ describe('HomeAssistant extension', () => {
'origin': origin,
'state_topic':'zigbee2mqtt/0x18fc26000000cafe',
'unique_id':'0x18fc26000000cafe_device_mode_zigbee2mqtt',
'value_template':'{{ value_json.device_mode }}'
'value_template':'{{ value_json.device_mode }}',
'enabled_by_default': true,
}
expect(MQTT.publish).toHaveBeenCalledWith(
"homeassistant/select/0x18fc26000000cafe/device_mode/config",
+3 -3
View File
@@ -25,7 +25,7 @@ describe('Bridge legacy', () => {
data.writeDefaultConfiguration();
settings.reRead();
logger.info.mockClear();
logger.warn.mockClear();
logger.warning.mockClear();
});
afterAll(async () => {
@@ -183,7 +183,7 @@ describe('Bridge legacy', () => {
const payload = JSON.parse(MQTT.publish.mock.calls[0][1]);
expect(payload.length).toStrictEqual(Object.values(zigbeeHerdsman.devices).length);
expect(payload[1]).toStrictEqual({"ieeeAddr": "0x00124b00120144ae", "type": "Coordinator", "dateCode": "20190425", "friendly_name": "Coordinator", networkAddress: 0, softwareBuildID: "z-Stack", lastSeen: 100});
expect(payload[2]).toStrictEqual({"dateCode": null, "friendly_name": "bulb", "ieeeAddr": "0x000b57fffec6a5b2", "lastSeen": 1000, "manufacturerID": 4476, "model": "LED1545G12", "modelID": "TRADFRI bulb E27 WS opal 980lm", "networkAddress": 40369, "powerSource": "Mains (single phase)", "softwareBuildID": null, "type": "Router", "description": "TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white", "vendor": "IKEA"});
expect(payload[2]).toStrictEqual({"dateCode": null, "friendly_name": "bulb", "ieeeAddr": "0x000b57fffec6a5b2", "lastSeen": 1000, "manufacturerID": 4476, "model": "LED1545G12", "modelID": "TRADFRI bulb E27 WS opal 980lm", "networkAddress": 40369, "powerSource": "Mains (single phase)", "softwareBuildID": null, "type": "Router", "description": "TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm", "vendor": "IKEA"});
Date.now = now;
});
@@ -478,6 +478,6 @@ describe('Bridge legacy', () => {
MQTT.events.message('zigbee2mqtt/bridge/config/touchlink/factory_reset', '');
await flushPromises();
expect(zigbeeHerdsman.touchlinkFactoryResetFirst).toHaveBeenCalledTimes(1);
expect(logger.warn).toHaveBeenCalledWith('Failed to factory reset device through Touchlink');
expect(logger.warning).toHaveBeenCalledWith('Failed to factory reset device through Touchlink');
});
});
+1 -1
View File
@@ -16,7 +16,7 @@ const Controller = require('../../lib/controller');
const flushPromises = require('../lib/flushPromises');
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
const mocksClear = [MQTT.publish, logger.warning, logger.debug];
describe('Report', () => {
let controller;
+190 -70
View File
@@ -4,38 +4,41 @@ let settings;
const fs = require('fs');
const path = require('path');
const data = require('./stub/data');
let stdOutWriteOriginal;
const {rimrafSync} = require('rimraf');
const Transport = require('winston-transport');
describe('Logger', () => {
let logger;
let consoleWriteSpy;
beforeAll(() => {
consoleWriteSpy = jest.spyOn(console._stdout, 'write').mockImplementation(() => {});
});
afterAll(() => {
consoleWriteSpy.mockRestore();
});
beforeEach(async () => {
data.writeDefaultConfiguration();
jest.resetModules();
settings = require('../lib/util/settings');
settings.set(['advanced', 'log_directory'], dir.name + '/%TIMESTAMP%');
settings.reRead();
stdOutWriteOriginal = console._stdout.write;
console._stdout.write = () => {};
logger = require('../lib/util/logger').default;
logger.init();
consoleWriteSpy.mockClear();
});
afterEach(async () => {
console._stdout.write = stdOutWriteOriginal;
});
it('Create log directory', () => {
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
const dirs = fs.readdirSync(dir.name);
expect(dirs.length).toBe(1);
});
it('Should cleanup', () => {
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
for (const d of fs.readdirSync(dir.name)) {
rimrafSync(path.join(dir.name, d));
}
@@ -50,54 +53,49 @@ describe('Logger', () => {
})
it('Should not cleanup when there is no timestamp set', () => {
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
for (let i = 30; i < 40; i++) {
fs.mkdirSync(path.join(dir.name, `log_${i}`));
}
settings.set(['advanced', 'log_directory'], dir.name + '/bla');
expect(fs.readdirSync(dir.name).length).toBe(20);
expect(fs.readdirSync(dir.name).length).toBe(21);
logger.cleanup();
expect(fs.readdirSync(dir.name).length).toBe(20);
expect(fs.readdirSync(dir.name).length).toBe(21);
})
it('Set and get log level', () => {
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
logger.setLevel('debug');
expect(logger.getLevel()).toBe('debug');
logger.setLevel('info');
expect(logger.getLevel()).toBe('info');
logger.setLevel('warning');
expect(logger.getLevel()).toBe('warning');
logger.setLevel('error');
expect(logger.getLevel()).toBe('error');
// winston level always stays at 'debug', logic handled by custom logger
expect(logger.winston.level).toStrictEqual('debug');
for (const transport of logger.winston.transports) {
expect(transport.level).toStrictEqual(undefined);
}
});
it('Add transport', () => {
it('Add/remove transport', () => {
class DummyTransport extends Transport {
log(info, callback) {
}
}
const logger = require('../lib/util/logger').default;
logger.init();
expect(logger.winston().transports.length).toBe(2);
logger.addTransport(new DummyTransport());
expect(logger.winston().transports.length).toBe(3);
});
it('Set and get log level warn <-> warning', () => {
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
logger.setLevel('warn');
expect(logger.winston().transports[0].level).toBe('warning');
expect(logger.getLevel()).toBe('warn');
expect(logger.winston.transports.length).toBe(2);
const transport = new DummyTransport();
logger.addTransport(transport);
expect(logger.winston.transports.length).toBe(3);
logger.removeTransport(transport);
expect(logger.winston.transports.length).toBe(2);
});
it('Logger should be console and file by default', () => {
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
const pipes = logger.winston()._readableState.pipes;
const pipes = logger.winston._readableState.pipes;
expect(pipes.length).toBe(2);
expect(pipes[0].constructor.name).toBe('Console');
expect(pipes[0].silent).toBe(false);
@@ -107,10 +105,8 @@ describe('Logger', () => {
it('Logger can be file only', () => {
settings.set(['advanced', 'log_output'], ['file']);
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
const pipes = logger.winston()._readableState.pipes;
const pipes = logger.winston._readableState.pipes;
expect(pipes.length).toBe(2);
expect(pipes[0].constructor.name).toBe('Console');
expect(pipes[0].silent).toBe(true);
@@ -120,30 +116,24 @@ describe('Logger', () => {
it('Logger can be console only', () => {
settings.set(['advanced', 'log_output'], ['console']);
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
const pipes = logger.winston()._readableState.pipes;
const pipes = logger.winston._readableState.pipes;
expect(pipes.constructor.name).toBe('Console');
expect(pipes.silent).toBe(false);
});
it('Logger can be nothing', () => {
settings.set(['advanced', 'log_output'], []);
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
const pipes = logger.winston()._readableState.pipes;
const pipes = logger.winston._readableState.pipes;
expect(pipes.constructor.name).toBe('Console');
expect(pipes.silent).toBe(true);
});
it('Should allow to disable log rotation', () => {
settings.set(['advanced', 'log_rotation'], false);
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
const pipes = logger.winston()._readableState.pipes;
const pipes = logger.winston._readableState.pipes;
expect(pipes[1].constructor.name).toBe('File');
expect(pipes[1].maxFiles).toBeNull();
expect(pipes[1].tailable).toBeFalsy();
@@ -152,36 +142,166 @@ describe('Logger', () => {
it('Should allow to symlink logs to current directory', () => {
settings.set(['advanced', 'log_symlink_current'], true);
let logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
expect(fs.readdirSync(dir.name).includes('current')).toBeTruthy()
jest.resetModules();
logger = require('../lib/util/logger').default;
});
it('Log', () => {
const logger = require('../lib/util/logger').default;
it.each([
['debug', {higher: ['info', 'warning', 'error'], lower: []}],
['info', {higher: ['warning', 'error'], lower: ['debug']}],
['warning', {higher: ['error'], lower: ['debug', 'info']}],
['error', {higher: [], lower: ['debug', 'info', 'warning']}],
])('Logs relevant levels for %s', (level, otherLevels) => {
logger.setLevel(level);
const logSpy = jest.spyOn(logger.winston, 'log');
consoleWriteSpy.mockClear();
let i = 1;
logger[level]('msg');
expect(logSpy).toHaveBeenLastCalledWith(level, 'msg', {namespace: 'z2m'});
expect(consoleWriteSpy).toHaveBeenCalledTimes(i++);
logger[level]('msg', 'abcd');
expect(logSpy).toHaveBeenLastCalledWith(level, 'msg', {namespace: 'abcd'});
expect(consoleWriteSpy).toHaveBeenCalledTimes(i++);
for (const higherLevel of otherLevels.higher) {
logger[higherLevel]('higher msg');
expect(logSpy).toHaveBeenLastCalledWith(higherLevel, 'higher msg', {namespace: 'z2m'});
expect(consoleWriteSpy).toHaveBeenCalledTimes(i++);
logger[higherLevel]('higher msg', 'abcd');
expect(logSpy).toHaveBeenLastCalledWith(higherLevel, 'higher msg', {namespace: 'abcd'});
expect(consoleWriteSpy).toHaveBeenCalledTimes(i++);
}
logSpy.mockClear();
consoleWriteSpy.mockClear();
for (const lowerLevel of otherLevels.lower) {
logger[lowerLevel]('lower msg');
expect(logSpy).not.toHaveBeenCalled();
expect(consoleWriteSpy).not.toHaveBeenCalled();
logger[lowerLevel]('lower msg', 'abcd');
expect(logSpy).not.toHaveBeenCalled();
expect(consoleWriteSpy).not.toHaveBeenCalled();
}
});
it('Logs Error object', () => {
const logSpy = jest.spyOn(logger.winston, 'log');
logger.error(new Error('msg'));// test for stack=true
expect(logSpy).toHaveBeenLastCalledWith('error', new Error('msg'), {namespace: 'z2m'});
expect(consoleWriteSpy).toHaveBeenCalledTimes(1);
})
it.each([
[
'^zhc:legacy:fz:(tuya|moes)',
new RegExp(/^zhc:legacy:fz:(tuya|moes)/),
[
{ ns: 'zhc:legacy:fz:tuya_device12', match: true },
{ ns: 'zhc:legacy:fz:moes_dimmer', match: true },
{ ns: 'zhc:legacy:fz:not_moes', match: false },
{ ns: 'zhc:legacy:fz', match: false },
{ ns: 'zhc:legacy:fz:', match: false },
{ ns: '1zhc:legacy:fz:tuya_device12', match: false },
]
],
[
'^zhc:legacy:fz:(tuya|moes)|^zh:ember:uart:|^zh:controller',
new RegExp(/^zhc:legacy:fz:(tuya|moes)|^zh:ember:uart:|^zh:controller/),
[
{ ns: 'zh:ember:uart:ash', match: true },
{ ns: 'zh:ember:uart', match: false },
{ ns: 'zh:controller', match: true },
{ ns: 'zh:controller:', match: true },
{ ns: 'azh:controller:', match: false },
]
],
[
'',
undefined,
[
{ ns: 'zhc:legacy:fz:tuya_device12', match: false },
{ ns: 'zhc:legacy:fz:moes_dimmer', match: false },
{ ns: 'zhc:legacy:fz:not_moes', match: false },
{ ns: 'zhc:legacy:fz', match: false },
{ ns: 'zhc:legacy:fz:', match: false },
{ ns: '1zhc:legacy:fz:tuya_device12', match: false },
{ ns: 'zh:ember:uart:ash', match: false },
{ ns: 'zh:ember:uart', match: false },
{ ns: 'zh:controller', match: false },
{ ns: 'zh:controller:', match: false },
{ ns: 'azh:controller:', match: false },
]
],
])('Sets namespace ignore for debug level %s', (ignore, expected, tests) => {
logger.setLevel('debug');
const logSpy = jest.spyOn(logger.winston, 'log');
logger.setDebugNamespaceIgnore(ignore);
expect(logger.debugNamespaceIgnoreRegex).toStrictEqual(expected);
expect(logger.getDebugNamespaceIgnore()).toStrictEqual(ignore);
for (const test of tests) {
logger.debug('Test message', test.ns);
if (test.match) {
expect(logSpy).not.toHaveBeenCalled();
} else {
expect(logSpy).toHaveBeenLastCalledWith('debug', 'Test message', {namespace: test.ns});
}
logSpy.mockClear();
}
});
it('Logs with namespaced levels or default - higher', () => {
settings.set(['advanced', 'log_namespaced_levels'], {
'z2m:mqtt': 'warning',
});
logger.init();
const warn = jest.spyOn(logger.winston(), 'warning');
logger.warn('warn');
expect(warn).toHaveBeenCalledWith('warn');
logger.setLevel('debug');
expect(logger.getNamespacedLevels()).toStrictEqual({"z2m:mqtt": 'warning'});
expect(logger.getLevel()).toStrictEqual('debug');
const debug = jest.spyOn(logger.winston(), 'debug');
logger.debug('debug');
expect(debug).toHaveBeenCalledWith('debug');
const logSpy = jest.spyOn(logger.winston, 'log');
const warning = jest.spyOn(logger.winston(), 'warning');
logger.warning('warning');
expect(warning).toHaveBeenCalledWith('warning');
consoleWriteSpy.mockClear();
logger.info(`MQTT publish: topic 'abcd/efgh', payload '{"my": {"payload": "injson"}}'`, 'z2m:mqtt');
expect(logSpy).toHaveBeenCalledTimes(0);
expect(consoleWriteSpy).toHaveBeenCalledTimes(0);
logger.error(`Not connected to MQTT server!`, 'z2m:mqtt');
expect(logSpy).toHaveBeenCalledTimes(1);
expect(consoleWriteSpy).toHaveBeenCalledTimes(1);
logger.info(`Just another info message`, 'z2m:notmqtt');
expect(logSpy).toHaveBeenCalledTimes(2);
expect(consoleWriteSpy).toHaveBeenCalledTimes(2);
});
const info = jest.spyOn(logger.winston(), 'info');
logger.info('info');
expect(info).toHaveBeenCalledWith('info');
it('Logs with namespaced levels or default - lower', () => {
expect(logger.getNamespacedLevels()).toStrictEqual({});
logger.setNamespacedLevels({'z2m:mqtt': 'info'})
logger.setLevel('warning');
expect(logger.getNamespacedLevels()).toStrictEqual({"z2m:mqtt": 'info'});
expect(logger.getLevel()).toStrictEqual('warning');
const error = jest.spyOn(logger.winston(), 'error');
logger.error('error');
expect(error).toHaveBeenCalledWith('error');
const logSpy = jest.spyOn(logger.winston, 'log');
consoleWriteSpy.mockClear();
logger.info(`MQTT publish: topic 'abcd/efgh', payload '{"my": {"payload": "injson"}}'`, 'z2m:mqtt');
expect(logSpy).toHaveBeenCalledTimes(1);
expect(consoleWriteSpy).toHaveBeenCalledTimes(1);
logger.error(`Not connected to MQTT server!`, 'z2m:mqtt');
expect(logSpy).toHaveBeenCalledTimes(2);
expect(consoleWriteSpy).toHaveBeenCalledTimes(2);
logger.info(`Just another info message`, 'z2m:notmqtt');
expect(logSpy).toHaveBeenCalledTimes(2);
expect(consoleWriteSpy).toHaveBeenCalledTimes(2);
logger.warning(`Just another warning message`, 'z2m:notmqtt');
expect(logSpy).toHaveBeenCalledTimes(3);
expect(consoleWriteSpy).toHaveBeenCalledTimes(3);
});
});
+9 -9
View File
@@ -17,10 +17,10 @@ const MQTT = require('./stub/mqtt');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = require('./lib/flushPromises');
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
const mocksClear = [MQTT.publish, logger.warning, logger.debug];
const setTimeoutNative = setTimeout;
describe('onlythisNetworkmap', () => {
describe('Networkmap', () => {
let controller;
beforeAll(async () => {
@@ -102,7 +102,7 @@ describe('onlythisNetworkmap', () => {
let call = MQTT.publish.mock.calls[0];
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/networkmap/raw');
const expected = {"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, color_options, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Wireless remote switch (double rocker), 2016 model","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Aqara"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":{"description":"Automatically generated definition","model":"notSupportedModelID","supports":"linkquality","vendor":"Boef"},"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]};
const expected = {"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, color_options, identify, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Wireless remote switch (double rocker), 2016 model","model":"WXKG02LM_rev1","supports":"battery, voltage, power_outage_count, action, linkquality","vendor":"Aqara"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":{"description":"Automatically generated definition","model":"notSupportedModelID","supports":"action, linkquality","vendor":"Boef"},"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"CC2530 router","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]};
expect(JSON.parse(call[1])).toStrictEqual(expected);
/**
@@ -143,7 +143,7 @@ describe('onlythisNetworkmap', () => {
const expected = `digraph G {
node[shape=record];
"0x00124b00120144ae" [style="bold, filled", fillcolor="#e04e5d", fontcolor="#ffffff", label="{Coordinator|0x00124b00120144ae (0x0000)|0 seconds ago}"];
"0x000b57fffec6a5b2" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{bulb|0x000b57fffec6a5b2 (0x9db1)|IKEA TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white (LED1545G12)|9 seconds ago}"];
"0x000b57fffec6a5b2" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{bulb|0x000b57fffec6a5b2 (0x9db1)|IKEA TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm (LED1545G12)|9 seconds ago}"];
"0x000b57fffec6a5b2" -> "0x00124b00120144ae" [penwidth=2, weight=1, color="#009900", label="92 (routes: 0x198c)"]
"0x000b57fffec6a5b3" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{bulb_color|0x000b57fffec6a5b3 (0x9dcf)|Philips Hue Go (7146060PH)|unknown}"];
"0x000b57fffec6a5b3" -> "0x00124b00120144ae" [penwidth=0.5, weight=0, color="#994444", label="120"]
@@ -151,7 +151,7 @@ describe('onlythisNetworkmap', () => {
"0x0017880104e45521" [style="rounded, dashed, filled", fillcolor="#fff8ce", fontcolor="#000000", label="{button_double_key|0x0017880104e45521 (0x198a)|Aqara Wireless remote switch (double rocker), 2016 model (WXKG02LM_rev1)|9 seconds ago}"];
"0x0017880104e45521" -> "0x0017880104e45559" [penwidth=1, weight=0, color="#994444", label="130"]
"0x0017880104e45525" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{0x0017880104e45525|0x0017880104e45525 (0x1988)failed: lqi,routingTable|Boef Automatically generated definition (notSupportedModelID)|9 seconds ago}"];
"0x0017880104e45559" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{cc2530_router|0x0017880104e45559 (0x198c)|Custom devices (DiY) [CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/) (CC2530.ROUTER)|9 seconds ago}"];
"0x0017880104e45559" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{cc2530_router|0x0017880104e45559 (0x198c)|Custom devices (DiY) CC2530 router (CC2530.ROUTER)|9 seconds ago}"];
"0x0017880104e45559" -> "0x000b57fffec6a5b2" [penwidth=0.5, weight=0, color="#994444", label="100"]
"0x0017880104e45511" [style="rounded, dashed, filled", fillcolor="#fff8ce", fontcolor="#000000", label="{0x0017880104e45511|0x0017880104e45511 (0x045a)|external external (external_converter_device)|9 seconds ago}"];
"0x0017880104e45511" -> "0x00124b00120144ae" [penwidth=1, weight=0, color="#994444", label="92"]
@@ -207,7 +207,7 @@ describe('onlythisNetworkmap', () => {
---
0x000b57fffec6a5b2 (0x9db1)
---
IKEA TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white (LED1545G12)
IKEA TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm (LED1545G12)
---
9 seconds ago
]
@@ -237,7 +237,7 @@ describe('onlythisNetworkmap', () => {
---
0x0017880104e45559 (0x198c)
---
Custom devices (DiY) [CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/) (CC2530.ROUTER)
Custom devices (DiY) CC2530 router (CC2530.ROUTER)
---
9 seconds ago
]
@@ -276,7 +276,7 @@ describe('onlythisNetworkmap', () => {
let call = MQTT.publish.mock.calls[0];
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
const expected = {"data":{"routes":true,"type":"raw","value":{"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, color_options, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Wireless remote switch (double rocker), 2016 model","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Aqara"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":{"description":"Automatically generated definition","model":"notSupportedModelID","supports":"linkquality","vendor":"Boef"},"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]}},"status":"ok"};
const expected = {"data":{"routes":true,"type":"raw","value":{"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, color_options, identify, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Wireless remote switch (double rocker), 2016 model","model":"WXKG02LM_rev1","supports":"battery, voltage, power_outage_count, action, linkquality","vendor":"Aqara"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":{"description":"Automatically generated definition","model":"notSupportedModelID","supports":"action, linkquality","vendor":"Boef"},"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"CC2530 router","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]}},"status":"ok"};
const actual = JSON.parse(call[1]);
expect(actual).toStrictEqual(expected);
});
@@ -303,7 +303,7 @@ describe('onlythisNetworkmap', () => {
let call = MQTT.publish.mock.calls[0];
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
const expected = {"data":{"routes":true,"type":"raw","value":{"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Wireless remote switch (double rocker), 2016 model","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Aqara"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":{"description":"Automatically generated definition","model":"notSupportedModelID","supports":"linkquality","vendor":"Boef"},"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]}},"status":"ok"}
const expected = {"data":{"routes":true,"type":"raw","value":{"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Wireless remote switch (double rocker), 2016 model","model":"WXKG02LM_rev1","supports":"battery, voltage, power_outage_count, action, linkquality","vendor":"Aqara"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":{"description":"Automatically generated definition","model":"notSupportedModelID","supports":"action, linkquality","vendor":"Boef"},"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"CC2530 router","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]}},"status":"ok"}
const actual = JSON.parse(call[1]);
expect(actual).toStrictEqual(expected);
});
+1 -1
View File
@@ -8,7 +8,7 @@ const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = require('./lib/flushPromises');
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
const mocksClear = [MQTT.publish, logger.warning, logger.debug];
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const mockOnEvent = jest.fn();
+16 -9
View File
@@ -72,7 +72,7 @@ describe('OTA update', () => {
mockClear(mapped);
logger.info.mockClear();
device.save.mockClear();
mapped.ota.updateToLatest.mockImplementationOnce((a, b, onUpdate) => {
mapped.ota.updateToLatest.mockImplementationOnce((a, onUpdate) => {
onUpdate(0, null);
onUpdate(10, 3600.2123);
return 90;
@@ -83,7 +83,7 @@ describe('OTA update', () => {
expect(logger.info).toHaveBeenCalledWith(`Updating 'bulb' to latest firmware`);
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(0);
expect(mapped.ota.updateToLatest).toHaveBeenCalledTimes(1);
expect(mapped.ota.updateToLatest).toHaveBeenCalledWith(device, logger, expect.any(Function));
expect(mapped.ota.updateToLatest).toHaveBeenCalledWith(device, expect.any(Function));
expect(logger.info).toHaveBeenCalledWith(`Update of 'bulb' at 0.00%`);
expect(logger.info).toHaveBeenCalledWith(`Update of 'bulb' at 10.00%, ≈ 60 minutes remaining`);
expect(logger.info).toHaveBeenCalledWith(`Finished update of 'bulb'`);
@@ -126,7 +126,7 @@ describe('OTA update', () => {
const mapped = await zigbeeHerdsmanConverters.findByDevice(device)
mockClear(mapped);
device.save.mockClear();
mapped.ota.updateToLatest.mockImplementationOnce((a, b, onUpdate) => {
mapped.ota.updateToLatest.mockImplementationOnce((a, onUpdate) => {
throw new Error('Update failed');
});
@@ -260,7 +260,7 @@ describe('OTA update', () => {
await zigbeeHerdsman.events.message(payload);
await flushPromises();
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(1);
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledWith(device, logger, {"imageType": 12382});
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledWith(device, {"imageType": 12382});
expect(logger.info).toHaveBeenCalledWith(`Update available for 'bulb'`);
expect(device.endpoints[0].commandResponse).toHaveBeenCalledTimes(1);
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 0x98}, undefined, 10);
@@ -294,7 +294,7 @@ describe('OTA update', () => {
await zigbeeHerdsman.events.message(payload);
await flushPromises();
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(1);
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledWith(device, logger, {"imageType": 12382});
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledWith(device, {"imageType": 12382});
expect(device.endpoints[0].commandResponse).toHaveBeenCalledTimes(1);
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 0x98}, undefined, 10);
expect(MQTT.publish).toHaveBeenCalledWith(
@@ -316,7 +316,7 @@ describe('OTA update', () => {
await zigbeeHerdsman.events.message(payload);
await flushPromises();
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(1);
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledWith(device, logger, {"imageType": 12382});
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledWith(device, {"imageType": 12382});
expect(device.endpoints[0].commandResponse).toHaveBeenCalledTimes(1);
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 0x98}, undefined, 10);
expect(MQTT.publish).toHaveBeenCalledWith(
@@ -374,7 +374,7 @@ describe('OTA update', () => {
logger.info.mockClear();
logger.error.mockClear();
device.save.mockClear();
mapped.ota.updateToLatest.mockImplementationOnce((a, b, onUpdate) => {
mapped.ota.updateToLatest.mockImplementationOnce((a, onUpdate) => {
onUpdate(0, null);
onUpdate(10, 3600);
return 91;
@@ -385,7 +385,7 @@ describe('OTA update', () => {
expect(logger.info).toHaveBeenCalledWith(`Updating 'bulb' to latest firmware`);
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(0);
expect(mapped.ota.updateToLatest).toHaveBeenCalledTimes(1);
expect(mapped.ota.updateToLatest).toHaveBeenCalledWith(device, logger, expect.any(Function));
expect(mapped.ota.updateToLatest).toHaveBeenCalledWith(device, expect.any(Function));
expect(logger.info).toHaveBeenCalledWith(`Update of 'bulb' at 0.00%`);
expect(logger.info).toHaveBeenCalledWith(`Update of 'bulb' at 10.00%, ≈ 60 minutes remaining`);
expect(logger.info).toHaveBeenCalledWith(`Finished update of 'bulb'`);
@@ -404,7 +404,7 @@ describe('OTA update', () => {
logger.info.mockClear();
logger.error.mockClear();
device.save.mockClear();
mapped.ota.updateToLatest.mockImplementationOnce((a, b, onUpdate) => {
mapped.ota.updateToLatest.mockImplementationOnce((a, onUpdate) => {
throw new Error('Update failed');
});
@@ -485,4 +485,11 @@ describe('OTA update', () => {
expect(spyUseIndexOverride).toHaveBeenCalledWith('http://my.site/index.json');
spyUseIndexOverride.mockClear();
});
it('Clear update state on startup', async () => {
const device = controller.zigbee.resolveEntity(zigbeeHerdsman.devices.bulb_color.ieeeAddr);
controller.state.set(device, {update: {progress: 100, remaining: 10, state: 'updating'}})
await resetExtension();
expect(controller.state.get(device)).toStrictEqual({update: {state: 'available'}});
});
});
+50 -5
View File
@@ -2,6 +2,7 @@ const data = require('./stub/data');
const sleep = require('./stub/sleep');
const logger = require('./stub/logger');
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
const {loadTopicGetSetRegex} = require('../lib/extension/publish');
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const stringify = require('json-stable-stringify-without-jsonify');
const MQTT = require('./stub/mqtt');
@@ -9,7 +10,7 @@ const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = require('./lib/flushPromises');
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
const mocksClear = [MQTT.publish, logger.warning, logger.debug];
const expectNothingPublished = () => {
Object.values(zigbeeHerdsman.devices).forEach((d) => {
@@ -40,6 +41,7 @@ describe('Publish', () => {
data.writeDefaultConfiguration();
controller.state.state = {};
settings.reRead();
loadTopicGetSetRegex();
mocksClear.forEach((m) => m.mockClear());
Object.values(zigbeeHerdsman.devices).forEach((d) => {
d.endpoints.forEach((e) => {
@@ -223,6 +225,40 @@ describe('Publish', () => {
);
});
it('Should publish messages to TuYa switch with dummy endpoints', async () => {
const device = zigbeeHerdsman.devices.TS0601_switch;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/TS0601_switch/set', stringify({state_l2: 'ON'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("manuSpecificTuya", "dataRequest", {dpValues: [{data: [1], datatype: 1, dp: 2}], seq: expect.any(Number)}, {disableDefaultResponse: true});
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/TS0601_switch',
stringify({state_l2: 'ON'}),
{ retain: false, qos: 0 },
expect.any(Function)
);
});
it('Should publish messages to TuYa cover switch with dummy endpoints', async () => {
const device = zigbeeHerdsman.devices.TS0601_cover_switch;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/TS0601_cover_switch/set', stringify({state: 'OPEN'}));
await MQTT.events.message('zigbee2mqtt/TS0601_cover_switch/set', stringify({state_l1: 'ON'}));
await MQTT.events.message('zigbee2mqtt/TS0601_cover_switch/l2/set', stringify({state: 'OFF'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(3);
expect(endpoint.command).toHaveBeenCalledWith("manuSpecificTuya", "dataRequest", {dpValues: [{data: [0], datatype: 4, dp: 1}], seq: expect.any(Number)}, {disableDefaultResponse: true});
expect(endpoint.command).toHaveBeenCalledWith("manuSpecificTuya", "dataRequest", {dpValues: [{data: [1], datatype: 1, dp: 102}], seq: expect.any(Number)}, {disableDefaultResponse: true});
expect(endpoint.command).toHaveBeenCalledWith("manuSpecificTuya", "dataRequest", {dpValues: [{data: [0], datatype: 1, dp: 101}], seq: expect.any(Number)}, {disableDefaultResponse: true});
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/TS0601_cover_switch',
stringify({state_l2: 'OFF', state_l1: 'ON', state: 'OPEN'}),
{ retain: false, qos: 0 },
expect.any(Function)
);
});
it('Should publish messages to zigbee devices with color xy', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
@@ -534,7 +570,7 @@ describe('Publish', () => {
logger.error.mockClear();
await MQTT.events.message('zigbee2mqtt/0x0017880104e45542/get', stringify({state_center: '', state_right: ''}));
await flushPromises();
expect(logger.error).toHaveBeenCalledWith(`Device 'wall_switch_double' has no endpoint 'center'`);
expect(logger.error).toHaveBeenCalledWith(`No converter available for 'state_center' ("")`);
expect(endpoint2.read).toHaveBeenCalledTimes(0);
expect(endpoint3.read).toHaveBeenCalledTimes(1);
expect(endpoint3.read).toHaveBeenCalledWith('genOnOff', ['onOff']);
@@ -560,6 +596,7 @@ describe('Publish', () => {
it('Should parse topic with when base topic has multiple slashes', async () => {
settings.set(['mqtt', 'base_topic'], 'zigbee2mqtt/at/my/home');
loadTopicGetSetRegex();
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/at/my/home/bulb_color/get', stringify({state: ''}));
@@ -580,6 +617,7 @@ describe('Publish', () => {
it('Should parse topic with when base and deviceID have multiple slashes', async () => {
settings.set(['mqtt', 'base_topic'], 'zigbee2mqtt/at/my/basement');
loadTopicGetSetRegex();
const device = zigbeeHerdsman.devices.bulb_color;
settings.set(['devices', device.ieeeAddr, 'friendly_name'], 'floor0/basement/my_device_id2');
const endpoint = device.getEndpoint(1);
@@ -678,6 +716,7 @@ describe('Publish', () => {
it('Should parse set with and slashes in base and deviceID postfix topic', async () => {
settings.set(['mqtt', 'base_topic'], 'zigbee2mqtt/at/my/home')
loadTopicGetSetRegex();
const device = zigbeeHerdsman.devices.QBKG03LM;
settings.set(['devices', device.ieeeAddr, 'friendly_name'], 'in/basement/wall_switch_double');
const endpoint = device.getEndpoint(2);
@@ -975,7 +1014,7 @@ describe('Publish', () => {
await MQTT.events.message('zigbee2mqtt/curtain/set', stringify(payload));
await flushPromises();
expect(endpoint.write).toHaveBeenCalledTimes(1);
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {"85": {"type": 57, "value": 100}});
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {presentValue: 100});
});
it('ZNCLDJ11LM position', async () => {
@@ -985,7 +1024,7 @@ describe('Publish', () => {
await MQTT.events.message('zigbee2mqtt/curtain/set', stringify(payload));
await flushPromises();
expect(endpoint.write).toHaveBeenCalledTimes(1);
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {"85": {"type": 57, "value": 10}});
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {presentValue: 10});
});
it('ZNCLDJ11LM position', async () => {
@@ -995,7 +1034,7 @@ describe('Publish', () => {
await MQTT.events.message('zigbee2mqtt/curtain/set', stringify(payload));
await flushPromises();
expect(endpoint.write).toHaveBeenCalledTimes(1);
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {"85": {"type": 57, "value": 0}});
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {presentValue: 0});
});
it('ZNCLDJ11LM position', async () => {
@@ -1502,4 +1541,10 @@ describe('Publish', () => {
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Log an error when entity is not found', async () => {
await MQTT.events.message('zigbee2mqtt/an_unknown_entity/set', stringify({}));
await flushPromises();
expect(logger.error).toHaveBeenCalledWith("Entity 'an_unknown_entity' is unknown");
});
});
+3 -2
View File
@@ -8,7 +8,7 @@ const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = require('./lib/flushPromises');
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
const mocksClear = [MQTT.publish, logger.warning, logger.debug];
describe('Receive', () => {
let controller;
@@ -467,10 +467,11 @@ describe('Receive', () => {
expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/SP600_OLD');
expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({energy: 6.65, power: 496});
});
it('Should emit DevicesChanged event when a converter announces changed exposes', async () => {
const device = zigbeeHerdsman.devices['BMCT-SLZ'];
const data = {deviceMode: 0}
const payload = {data, cluster: 'manuSpecificBosch10', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
const payload = {data, cluster: 'boschSpecific', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
await zigbeeHerdsman.events.message(payload);
expect(MQTT.publish.mock.calls[0][0]).toStrictEqual("zigbee2mqtt/bridge/devices");
});
+10 -12
View File
@@ -882,18 +882,6 @@ describe('Settings', () => {
expect(settings.validate()).toEqual(expect.arrayContaining([error]));
});
it('Configuration shouldnt be valid when friendly_name is a postfix', async () => {
write(configurationFile, {
...minimalConfig,
devices: {'0x0017880104e45519': {friendly_name: 'left', retain: false}},
});
settings.reRead();
const error = `Following friendly_name are not allowed: '${utils.endpointNames}'`;
expect(settings.validate()).toEqual(expect.arrayContaining([error]));
});
it('Configuration shouldnt be valid when duplicate friendly_name are used', async () => {
write(configurationFile, {
devices: {
@@ -1008,4 +996,14 @@ describe('Settings', () => {
expect(settings.get().blocklist).toStrictEqual(['blocklist', 'ban'])
expect(settings.get().passlist).toStrictEqual(['passlist', 'whitelist'])
});
it('Deprecated: warn log level', () => {
write(configurationFile, {...minimalConfig,
advanced: {log_level: 'warn'}
});
settings.reRead();
expect(settings.get().advanced.log_level).toStrictEqual('warning')
})
});
+6
View File
@@ -179,6 +179,12 @@ function writeDefaultConfiguration() {
'0x0017882104a44559': {
friendly_name: 'TS0601_thermostat',
},
'0x0017882104a44560': {
friendly_name: 'TS0601_switch',
},
'0x0017882104a44562': {
friendly_name: 'TS0601_cover_switch',
},
'0x0017882194e45543': {
friendly_name: 'QS-Zigbee-D02-TRIAC-2C-LN',
},
+16 -7
View File
@@ -1,28 +1,37 @@
let level = 'info';
let debugNamespaceIgnore = '';
let namespacedLevels = {};
const transports = [];
let transports = [];
let transportsEnabled = false;
const callTransports = (level, message) => {
const callTransports = (level, message, namespace) => {
if (transportsEnabled) {
for (const transport of transports) {
transport.log({level, message}, () => {});
transport.log({level, message, namespace}, () => {});
}
}
}
const mock = {
init: jest.fn(),
info: jest.fn().mockImplementation((msg) => callTransports('info', msg)),
warn: jest.fn().mockImplementation((msg) => callTransports('warn', msg)),
error: jest.fn().mockImplementation((msg) => callTransports('error', msg)),
debug: jest.fn().mockImplementation((msg) => callTransports('debug', msg)),
info: jest.fn().mockImplementation((msg, namespace = 'z2m') => callTransports('info', msg, namespace)),
warning: jest.fn().mockImplementation((msg, namespace = 'z2m') => callTransports('warning', msg, namespace)),
error: jest.fn().mockImplementation((msg, namespace = 'z2m') => callTransports('error', msg, namespace)),
debug: jest.fn().mockImplementation((msg, namespace = 'z2m') => callTransports('debug', msg, namespace)),
cleanup: jest.fn(),
logOutput: jest.fn(),
add: (transport) => transports.push(transport),
addTransport: (transport) => transports.push(transport),
removeTransport: (transport) => {
transports = transports.filter((t) => t !== transport);
},
setLevel: (newLevel) => {level = newLevel},
getLevel: () => level,
setNamespacedLevels: (nsLevels) => {namespacedLevels = nsLevels},
getNamespacedLevels: () => namespacedLevels,
setDebugNamespaceIgnore: (newIgnore) => {debugNamespaceIgnore = newIgnore},
getDebugNamespaceIgnore: () => debugNamespaceIgnore,
setTransportsEnabled: (value) => {transportsEnabled = value},
end: jest.fn(),
};
+1
View File
@@ -4,6 +4,7 @@ const mock = {
publish: jest.fn().mockImplementation((topic, payload, options, cb) => cb()),
end: jest.fn(),
subscribe: jest.fn(),
unsubscribe: jest.fn(),
reconnecting: false,
on: jest.fn(),
stream: {setMaxListeners: jest.fn()}
+26 -4
View File
@@ -34,6 +34,20 @@ const clusters = {
'msCO2': 1037
}
const custom_clusters = {
custom_1: {
ID: 64672,
manufacturerCode: 4617,
attributes: {
attribute_0: {ID: 0, type: 49},
},
commands: {
command_0: { ID: 0, response: 0, parameters: [{name: 'reset', type: 40}], },
},
commandsResponse: {},
},
}
class Endpoint {
constructor(ID, inputClusters, outputClusters, deviceIeeeAddress, binds=[], clusterValues={}, configuredReportings=[], profileID=null, deviceID=null, meta={}) {
this.deviceIeeeAddress = deviceIeeeAddress;
@@ -102,7 +116,7 @@ class Endpoint {
}
class Device {
constructor(type, ieeeAddr, networkAddress, manufacturerID, endpoints, interviewCompleted, powerSource = null, modelID = null, interviewing=false, manufacturerName, dateCode= null, softwareBuildID=null) {
constructor(type, ieeeAddr, networkAddress, manufacturerID, endpoints, interviewCompleted, powerSource = null, modelID = null, interviewing=false, manufacturerName, dateCode= null, softwareBuildID=null, customClusters = {}) {
this.type = type;
this.ieeeAddr = ieeeAddr;
this.dateCode = dateCode;
@@ -113,11 +127,14 @@ class Device {
this.softwareBuildID = softwareBuildID;
this.interviewCompleted = interviewCompleted;
this.modelID = modelID;
this.interview = jest.fn();
this.interviewing = interviewing;
this.meta = {};
this.ping = jest.fn();
this.removeFromNetwork = jest.fn();
this.removeFromDatabase = jest.fn();
this.customClusters = customClusters;
this.addCustomCluster = jest.fn();
this.save = jest.fn();
this.manufacturerName = manufacturerName;
this.lastSeen = 1000;
@@ -134,6 +151,8 @@ const bulb_color = new Device('Router', '0x000b57fffec6a5b3', 40399, 4107, [new
const bulb_color_2 = new Device('Router', '0x000b57fffec6a5b4', 401292, 4107, [new Endpoint(1, [0,3,4,5,6,8,768,2821,4096], [5,25,32,4096], '0x000b57fffec6a5b4', [], {lightingColorCtrl: {colorCapabilities: 254}}, [], null, null, {'scenes': {'1_0': {name: 'Chill scene', state: {state: 'ON'}}, '4_9': {state: {state: 'OFF'}}}})], true, "Mains (single phase)", "LLC020", false, 'Philips', '2019.09', '5.127.1.26581');
const bulb_2 = new Device('Router', '0x000b57fffec6a5b7', 40369, 4476, [new Endpoint(1, [0,3,4,5,6,8,768,2821,4096], [5,25,32,4096], '0x000b57fffec6a5b7', [], {lightingColorCtrl: {colorCapabilities: 17}})], true, "Mains (single phase)", "TRADFRI bulb E27 WS opal 980lm");
const TS0601_thermostat = new Device('EndDevice', '0x0017882104a44559', 6544,4151, [new Endpoint(1, [], [], '0x0017882104a44559')], true, "Mains (single phase)", 'kud7u2l');
const TS0601_switch = new Device('EndDevice', '0x0017882104a44560', 6544,4151, [new Endpoint(1, [], [], '0x0017882104a44560')], true, "Mains (single phase)", 'kjintbl');
const TS0601_cover_switch = new Device('EndDevice', '0x0017882104a44562', 6544,4151, [new Endpoint(1, [], [], '0x0017882104a44562')], true, "Mains (single phase)", 'TS0601', false, '_TZE200_5nldle7w');
const ZNCZ02LM = new Device('Router', '0x0017880104e45524', 6540,4151, [new Endpoint(1, [0, 6], [], '0x0017880104e45524')], true, "Mains (single phase)", "lumi.plug");
const GLEDOPTO_2ID = new Device('Router', '0x0017880104e45724', 6540,4151, [new Endpoint(11, [0,3,4,5,6,8,768], [], '0x0017880104e45724', [], {}, [], 49246, 528), new Endpoint(12, [0, 3, 4, 5, 6, 8, 768], [], '0x0017880104e45724', [], {}, [], 260, 258), new Endpoint(13, [4096], [4096], '0x0017880104e45724', [], {}, [], 49246, 57694), new Endpoint(15, [0, 3, 4, 5, 6, 8, 768], [], '0x0017880104e45724', [], {}, [], 49246, 256)], true, "Mains (single phase)", 'GL-C-007', false, 'GLEDOPTO');
const QBKG03LM = new Device('Router', '0x0017880104e45542', 6540,4151, [new Endpoint(1, [0], [], '0x0017880104e45542'), new Endpoint(2, [0, 6], [], '0x0017880104e45542'), new Endpoint(3, [0, 6], [], '0x0017880104e45542')], true, "Mains (single phase)", 'lumi.ctrl_neutral2');
@@ -197,6 +216,8 @@ const devices = {
'3157100': new Device('Router', '0x0017880104e44559', 6542,4151, [new Endpoint(1, [], [], '0x0017880104e44559')], true, "Mains (single phase)", '3157100', false, 'Centralite'),
'J1': new Device('Router', '0x0017880104a44559', 6543,4151, [new Endpoint(1, [], [], '0x0017880104a44559')], true, "Mains (single phase)", 'J1 (5502)'),
'TS0601_thermostat': TS0601_thermostat,
'TS0601_switch': TS0601_switch,
'TS0601_cover_switch': TS0601_cover_switch,
'external_converter_device': new Device('EndDevice', '0x0017880104e45511', 1114, 'external', [new Endpoint(1, [], [], '0x0017880104e45511')], false, null, 'external_converter_device' ),
'QS_Zigbee_D02_TRIAC_2C_LN':new Device('Router', '0x0017882194e45543', 6549,4151, [new Endpoint(1, [0], [], '0x0017882194e45543'), new Endpoint(2, [0, 6], [], '0x0017882194e45543')], true, "Mains (single phase)", 'TS110F', false, '_TYZB01_v8gtiaed'),
'unknown': new Device('Router', '0x0017980134e45545', 6540,4151, [], true, "Mains (single phase)"),
@@ -205,7 +226,8 @@ const devices = {
'bj_scene_switch': new Device('EndDevice', '0xd85def11a1002caa', 50117, 4398, [new Endpoint(10, [0,4096], [3,4,5,6,8,25,768,4096], '0xd85def11a1002caa', [{target: bulb_color_2.endpoints[0], cluster: {ID: 8, name: 'genLevelCtrl'}}, {target: bulb_color_2.endpoints[0], cluster: {ID: 6, name: 'genOnOff'}}, {target: bulb_color_2.endpoints[0], cluster: {ID: 768, name: 'lightingColorCtrl'}},]), new Endpoint(11, [0,4096], [3,4,5,6,8,25,768,4096], '0xd85def11a1002caa')], true, 'Battery', 'RB01', false, 'Busch-Jaeger', '20161222', '1.2.0'),
'GW003-AS-IN-TE-FC': new Device('Router', '0x0017548104a44669', 6545,4699, [new Endpoint(1, [3], [0,3,513,514], '0x0017548104a44669')], true, "Mains (single phase)", 'Adapter Zigbee FUJITSU'),
'BMCT-SLZ': new Device('Router', '0x18fc26000000cafe', 6546,4617, [new Endpoint(1, [0,3,4,5,258,1794,2820,2821,64672], [10,25], '0x18fc26000000cafe')], true, "Mains (single phase)", 'RBSH-MMS-ZB-EU'),
'BMCT_SLZ': new Device('Router', '0x0026decafe000473', 6546,4617, [new Endpoint(1, [0,3,4,5,258,1794,2820,2821,64672], [10,25], '0x0026decafe000473')], true, "Mains (single phase)", 'RBSH-MMS-ZB-EU', false, null, null, null, custom_clusters),
'bulb_custom_cluster': new Device('Router', '0x000b57fffec6a5c2', 40369, 4476, [new Endpoint(1, [0,3,4,5,6,8,768,2821,4096], [5,25,32,4096], '0x000b57fffec6a5c2')], true, "Mains (single phase)", "TRADFRI bulb E27 WS opal 980lm", false, null, null, null, custom_clusters),
}
const mock = {
@@ -257,10 +279,10 @@ const mock = {
const mockConstructor = jest.fn().mockImplementation(() => mock);
jest.mock('zigbee-herdsman', () => ({
...jest.requireActual('zigbee-herdsman'),
Controller: mockConstructor,
Zcl: {ManufacturerCode: {Philips: 4107}},
}));
module.exports = {
events, ...mock, constructor: mockConstructor, devices, groups, returnDevices
events, ...mock, constructor: mockConstructor, devices, groups, returnDevices, custom_clusters
};
+17 -13
View File
@@ -1,18 +1,24 @@
#!/bin/bash -e
cd "$(dirname "$0")"
NEED_RESTART=0
if [ -d data-backup ]; then
echo "ERROR: Backup directory exists. May be previous restoring was failed?"
echo "1. Save 'data-backup' and 'data' dirs to safe location to make possibility to restore config later."
echo "2. Manually delete 'data-backup' dir and try again."
exit 1
echo "ERROR: Backup directory exists. May be previous restoring was failed?"
echo "1. Save 'data-backup' and 'data' dirs to safe location to make possibility to restore config later."
echo "2. Manually delete 'data-backup' dir and try again."
exit 1
fi
if which systemctl 2> /dev/null > /dev/null; then
echo "Stopping Zigbee2MQTT..."
sudo systemctl stop zigbee2mqtt
echo "Checking Zigbee2MQTT status..."
if systemctl is-active --quiet zigbee2mqtt; then
echo "Stopping Zigbee2MQTT..."
sudo systemctl stop zigbee2mqtt
NEED_RESTART=1
fi
else
echo "Skipped stopping Zigbee2MQTT, no systemctl found"
echo "Skipped stopping Zigbee2MQTT, no systemctl found"
fi
echo "Creating backup of configuration..."
@@ -22,7 +28,7 @@ echo "Checking out changes to package-lock.json..."
git checkout package-lock.json
echo "Updating..."
git pull
git pull --no-rebase
echo "Installing dependencies..."
npm ci
@@ -34,11 +40,9 @@ echo "Restore configuration..."
cp -R data-backup/* data
rm -rf data-backup
if which systemctl 2> /dev/null > /dev/null; then
echo "Starting Zigbee2MQTT..."
sudo systemctl start zigbee2mqtt
else
echo "Skipped starting Zigbee2MQTT, no systemctl found"
if [ $NEED_RESTART -eq 1 ]; then
echo "Starting Zigbee2MQTT..."
sudo systemctl start zigbee2mqtt
fi
echo "Done!"