Compare commits

..
1456 Commits
Author SHA1 Message Date
koenkk 4ccfdfc6f4 chore: release 1.32.1 2023-07-08 09:32:42 +02:00
koenkk 15601d6339 fix(ignore): Update zigbee-herdsman-converters to 15.33.1-hotfix.0 2023-07-08 09:21:42 +02:00
Koen Kanters 69638d7956 chore(dev): release 1.32.0 (#18163) 2023-07-01 12:50:40 +02:00
koenkk 953f3a232b chore: Fix release please 2023-07-01 12:25:11 +02:00
Koen KantersandKoenkk d1824f169b fix(ignore): update zigbee-herdsman-converters to 15.33.1 (#18161)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-07-01 06:37:59 +00:00
Koen KantersandKoenkk 81973c3a2a fix(ignore): update zigbee-herdsman-converters to 15.33.0 (#18156)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-30 17:05:35 +00:00
Koen Kanters 213e2b50d6 chore: merge master to dev 2023-06-30 18:52:54 +02:00
gruijter 0a63ae5376 chore: Added Homey Integration to README.md (#18152) 2023-06-30 18:52:33 +02:00
Koen KantersandKoenkk 61dd1dd96d fix(ignore): update zigbee-herdsman-converters to 15.32.0 (#18132)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-27 19:05:48 +00:00
Koen KantersandKoenkk ed85367e53 fix(ignore): update zigbee-herdsman-converters to 15.31.0 (#18125)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-26 17:47:42 +00:00
Koen KantersandKoenkk 38c2ea5bf7 fix(ignore): update zigbee-herdsman to 0.16.0 (#18124)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-26 17:40:00 +00:00
Koen KantersandKoenkk 2d62027e24 fix(ignore): update zigbee-herdsman to 0.15.3 (#18116)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-25 10:00:07 +02:00
Koen KantersandKoenkk a56719d19c fix(ignore): update zigbee-herdsman-converters to 15.30.0 (#18115)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-25 09:59:01 +02:00
Koen Kanters 94744525ba feat: Support settable Home Assistant text (#18114) 2023-06-25 09:23:13 +02:00
koenkk ea193f1938 Revert "fix(ignore): update dependencies (#18112)"
This reverts commit 57566c76ed.
2023-06-25 09:19:27 +02:00
koenkk 88339473ce fix(ignore): Fix DeviceMessage.meta type 2023-06-25 09:16:36 +02:00
github-actions[bot]andKoenkk 57566c76ed fix(ignore): update dependencies (#18112)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-25 08:12:19 +02:00
koenkk a09342836b chore: Improve changelog generator 2023-06-23 18:24:12 +02:00
Koen Kanters b3fbf78bec chore: Fix changelog generator cache (#18100)
* chore: Fix changelog

* u

* u

* u

* u

* u
2023-06-23 18:14:35 +02:00
Koen KantersandKoenkk a744838e9c fix(ignore): update zigbee-herdsman-converters to 15.29.0 (#18099)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-23 16:06:47 +00:00
koenkk 712c7bdbaf chore: Cache commit users in changelog generator 2023-06-22 21:10:10 +02:00
Koen KantersandKoenkk 6d3389b409 fix(ignore): update zigbee-herdsman-converters to 15.28.0 (#18092)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-22 18:46:31 +00:00
Koen KantersandKoenkk 03005b38c8 fix(ignore): update zigbee-herdsman to 0.15.2 (#18091)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-22 18:37:39 +00:00
Koen KantersandKoenkk 17cff2a26f fix(ignore): update zigbee-herdsman to 0.15.1 (#18080)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-21 17:32:24 +00:00
Koen KantersandKoenkk 5777d76e55 fix(ignore): update zigbee-herdsman-converters to 15.27.0 (#18075)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-20 17:18:43 +00:00
Koen KantersandKoenkk 91d67a0ef5 fix(ignore): update zigbee-herdsman-converters to 15.26.0 (#18067)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-19 19:09:38 +00:00
Grégoire SeuxandKoen Kanters e083a6bff7 feat: Support Home Assistant water device_class (#18066)
This should be sufficient for https://www.zigbee2mqtt.io/devices/ZVG1.html to be water_consumed sensor to be usable in Energy dashboard (water section).

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2023-06-19 19:21:43 +02:00
Koen KantersandKoenkk 57aaa5a85a fix(ignore): update zigbee-herdsman-converters to 15.25.0 (#18056)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-18 10:03:48 +00:00
koenkk 34bad92ec6 chore: fix update_deps 2023-06-18 11:00:53 +02:00
Koen Kanters b6a4acda9b Revert "fix(ignore): update dependencies (#18052)" (#18053)
This reverts commit 1dc0b24e9a.
2023-06-18 10:56:59 +02:00
github-actions[bot]andKoenkk 1dc0b24e9a fix(ignore): update dependencies (#18052)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-18 10:40:32 +02:00
dependabot[bot] 38de19bb2c chore: Bump alpine from 3.18.0 to 3.18.2 in /docker (#18030) 2023-06-15 18:56:06 +02:00
Koen KantersandKoenkk c47f5df021 fix(ignore): update zigbee-herdsman-converters to 15.24.0 (#18025)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-14 18:31:21 +00:00
Koen KantersandKoenkk 4ad838c114 fix(ignore): update zigbee-herdsman-converters to 15.23.0 (#18015)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-13 19:41:02 +00:00
Koen KantersandKoenkk b0b3aee648 fix(ignore): update zigbee-herdsman-converters to 15.22.0 (#18004)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-12 16:26:26 +00:00
koenkk 446dbbf1fc fix(ignore): Fix e10dd893ab 2023-06-12 18:22:15 +02:00
koenkk 11f0be55d8 fix: Fix Home Assistant truncate error when program is null. https://github.com/Koenkk/zigbee2mqtt/issues/16460 2023-06-12 18:10:45 +02:00
github-actions[bot]andKoenkk e10dd893ab fix(ignore): update dependencies (#17991)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-12 17:22:42 +02:00
Koen KantersandKoenkk 0dd899679d Update zigbee-herdsman-converters to 15.21.2 (#17967)
* fix(ignore): update zigbee-herdsman-converters to 15.21.2

* fix

---------

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-08 21:42:29 +00:00
Koen KantersandKoenkk 05cab61e1f fix(ignore): update zigbee-herdsman to 0.15.0 (#17966)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-08 23:10:44 +02:00
Koen KantersandKoenkk 0c7e7e5089 fix(ignore): update zigbee-herdsman-converters to 15.20.0 (#17954)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-08 07:25:05 +00:00
koenkk 5f71776434 chore: improve changelog generator 2023-06-08 09:20:42 +02:00
Koen Kanters a7e02a7be8 fix(ignore): Better fix for #17891 (#17951)
* fix(ignore): Better fix for #17891

* remove onlythis
2023-06-08 09:04:14 +02:00
koenkk 7df6b2bab5 chore: Merge branch 'master' into dev 2023-06-07 20:24:57 +02:00
koenkk 21f51258a9 chore: release 1.31.2 2023-06-07 20:17:06 +02:00
koenkk c6904686c3 fix: Update zhc to 15.13.1-hotfix.1 2023-06-07 20:16:23 +02:00
koenkk a1bea8c5ab chore: Fix changelog generator 2023-06-07 16:48:26 +02:00
Koen KantersandKoenkk c23d244405 Update zigbee-herdsman-converters to 15.19.1 (#17941)
* fix(ignore): update zigbee-herdsman-converters to 15.19.1

* fix

---------

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-07 14:30:11 +00:00
koenkk 86898e5ceb chore: Merge branch 'master' into dev 2023-06-07 14:57:48 +02:00
koenkk 3572c60770 chore: release 1.31.1 2023-06-07 14:55:08 +02:00
koenkk ffc7ab83a2 fix: Update zhc to 15.13.1-hotfix.0 2023-06-07 14:54:34 +02:00
Sean Kelly c12bedb3e5 fix: Fix HA entities unavailable after broker restart when broker does not persist retained messages (#17891)
* Fix race condition with bridge status message cancelling reinitialize after a reconnect to mqtt

* Fix tests
2023-06-07 09:16:11 +02:00
Koen KantersandKoenkk fd1622ba80 fix(ignore): update zigbee-herdsman-converters to 15.18.0 (#17917)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-05 08:44:21 +00:00
Koen KantersandKoenkk 7564019ecd fix(ignore): update zigbee-herdsman-converters to 15.17.0 (#17909)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-04 12:18:21 +00:00
koenkk 6d381da075 chore: fix ci.yml 2023-06-04 14:01:49 +02:00
github-actions[bot]andKoenkk b10aa83fc8 fix(ignore): update dependencies (#17903)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-04 13:47:46 +02:00
Koen KantersandKoenkk a7997a3cd4 fix(ignore): update zigbee-herdsman-converters to 15.16.0 (#17900)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-03 16:07:49 +00:00
Koen KantersandKoenkk 73c19bff71 fix(ignore): update zigbee-herdsman-converters to 15.15.1 (#17884)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-02 17:58:16 +00:00
Koen KantersandKoenkk 80aa21123c fix(ignore): update zigbee-herdsman-converters to 15.15.0 (#17872)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-02 07:52:26 +00:00
koenkk afd1760b6f chore: Fix changelog generator 2023-06-02 09:47:58 +02:00
Koen KantersandKoenkk ddcab827ad Update zigbee-herdsman-converters to 15.14.1 (#17859)
* fix(ignore): update zigbee-herdsman-converters to 15.14.1

* fix tests

---------

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-01 14:29:42 +00:00
koenkk d0a41548fd chore: Exclude node_modules from TS compiler 2023-06-01 16:10:05 +02:00
koenkk ff8e6b9c66 chore: fix release_please.yml 2023-06-01 15:25:20 +02:00
koenkk 25f30a94f5 Revert "chore: promote to dev"
This reverts commit efe451df79.
2023-06-01 15:15:24 +02:00
Koenkk efe451df79 chore: promote to dev 2023-06-01 13:13:42 +00:00
koenkk b97a09c75b chore: improve release_please.yml 2023-06-01 14:39:25 +02:00
koenkk f3b8c689a9 chore: improve release_please.yml 2023-06-01 14:36:39 +02:00
koenkk 4d1a8ee873 chore: promote to dev 2023-06-01 11:30:16 +02:00
Koen Kanters f440c86019 chore(dev): release 1.31.0 (#17772) 2023-06-01 11:06:39 +02:00
Koen KantersandKoenkk 4b20dfafab fix(ignore): update zigbee-herdsman-converters to 15.13.1 (#17851)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-06-01 08:34:42 +00:00
Koen KantersandKoenkk b228d9daf6 fix(ignore): update zigbee-herdsman-converters to 15.13.0 (#17842)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-31 08:10:14 +00:00
Koen KantersandKoenkk be82b737be fix(ignore): update zigbee-herdsman-converters to 15.12.0 (#17837)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-30 09:50:17 +02:00
Koen Kanters d119f609de fix(ignore): Fix build (#17838) 2023-05-30 09:50:05 +02:00
github-actions[bot]andKoenkk a71857c723 fix(ignore): update dependencies (#17811)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-28 14:27:10 +02:00
Koen Kanters a404f9ff92 chore: merge master to dev 2023-05-27 16:36:37 +02:00
koenkk 0cbbf40040 chore: improve merge_master_to_dev.yml 2023-05-27 16:36:05 +02:00
Koen Kanters 277ac10e44 2023-05-27 16:32:16 +02:00
github-actions[bot]andKoenkk 2e8dfbb96e fix(ignore): update zigbee-herdsman-converters to 15.11.0 (#17808)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-27 16:30:07 +02:00
koenkk 366642cf71 chore: improve ci/cd 2023-05-27 16:29:57 +02:00
github-actions[bot]andKoenkk b7b3b51cfe fix(ignore): update zigbee-herdsman-converters to 15.10.0 (#17797)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-26 10:11:03 +02:00
koenkk e6f9aeda06 fix: Fix Home Assistant Payload is not supported (e.g. open, closed, opening, closing, stopped): STOP error. https://github.com/Koenkk/zigbee2mqtt/issues/17552 2023-05-26 09:32:29 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> bad365b9a7 chore: Bump actions/download-artifact from 2 to 3 (#17793)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-26 09:10:58 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1f8a68d4c9 chore: Bump actions/upload-artifact from 2 to 3 (#17794)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-26 09:10:44 +02:00
koenkk 443d5ea43c chore: fix ci.yml 2023-05-25 10:29:18 +02:00
koenkk af0cea0fb9 chore: fix release_please.yml 2023-05-25 10:25:27 +02:00
Koen Kanters 3701cce15e 2023-05-25 10:23:39 +02:00
github-actions[bot]andKoenkk 5b807c36ec fix(ignore): update zigbee-herdsman-converters to 15.9.0 (#17791)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-25 10:23:22 +02:00
koenkk 7cea62995c chore: fix ci.yml 2023-05-25 10:23:12 +02:00
koenkk 2a7d49b709 chore: automate releasing 2023-05-25 10:12:46 +02:00
Koen Kanters 1b7138e0c8 2023-05-25 09:19:40 +02:00
koenkk 0476b3362e Add merge_master_to_dev.yml 2023-05-25 09:19:05 +02:00
koenkk 3ed7ac90c9 chore: fix generateChangelog.js and ci.yml 2023-05-24 16:56:08 +02:00
github-actions[bot]andKoenkk 8b579b6533 fix(ignore): update zigbee-herdsman-converters to 15.8.2 (#17789)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-24 16:41:06 +02:00
koenkk 2d731ba1b6 chore: fix release_please.yml 2023-05-24 13:13:41 +02:00
koenkk 6a80d187fd Merge branch 'master' into dev 2023-05-24 13:11:44 +02:00
koenkk 1e8c3d05e5 chore: update ci config 2023-05-24 13:09:38 +02:00
koenkk ce7f09afc6 Update update_dependency.yml 2023-05-24 09:04:36 +02:00
koenkk c0e8efcde5 Add update_dependency.yml 2023-05-24 09:00:44 +02:00
github-actions[bot]andKoenkk 1344ae0616 feat(ignore): update zigbee-herdsman-converters to 15.7.0 (#17777)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-23 11:43:05 +00:00
github-actions[bot]andKoenkk 24baed3053 feat(ignore): update zigbee-herdsman-converters to 15.6.0 (#17774)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-23 12:45:35 +02:00
koenkk 8e95061fe5 Update release_please config 2023-05-23 12:17:48 +02:00
koenkk 626b2c47c1 Update config.yml 2023-05-23 12:15:15 +02:00
koenkk 95dff212e7 Update config.yml 2023-05-23 12:13:47 +02:00
koenkk c6416c9c61 Fix config.yml 2023-05-23 12:12:13 +02:00
koenkk 1898140f64 Update config.yml 2023-05-23 12:11:44 +02:00
github-actions[bot]andKoenkk 8457c3735b fix(ignore): update zigbee-herdsman to 0.14.117 (#17770)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-23 11:38:29 +02:00
koenkk 47c08b6cbd Merge branch 'master' into dev 2023-05-22 18:46:17 +02:00
koenkk b3de973af6 feat(ignore): update CI/CD 2023-05-22 18:45:47 +02:00
koenkk 0d17d26133 feat(ignore): update CI/CD 2023-05-22 18:38:28 +02:00
koenkk c802e776d6 Merge branch 'master' into dev 2023-05-22 16:56:42 +02:00
github-actions[bot]andKoenkk aa0f78269f chore: update zigbee-herdsman-converters to 15.5.0 (#17754)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-22 14:50:28 +00:00
koenkk 97b14cc2d4 Add release-please 2023-05-22 16:41:31 +02:00
github-actions[bot]andKoenkk d952ee60a9 Update dependencies (#17739)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-22 13:31:28 +02:00
github-actions[bot]andKoenkk 3d3bff88d7 Update zigbee-herdsman-converters to 15.0.117 (#17750)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-22 08:40:10 +00:00
github-actions[bot]andKoenkk c1cb4697f7 Update zigbee-herdsman-converters to 15.0.116 (#17736)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-20 12:55:49 +00:00
github-actions[bot]andKoenkk bfc51e0dc5 Update zigbee-herdsman-converters to 15.0.115 (#17700)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-16 17:55:56 +00:00
github-actions[bot]andKoenkk d339460269 Update zigbee-herdsman-converters to 15.0.114 (#17691)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-15 17:44:34 +00:00
Pierre-Gilles Leymarie fb60deeebc Add Gladys Assistant compatibility in README (#17686)
* Add Gladys Assistant integration in README

* Fix indentation

* Add <br> to align items

* Test with just one br
2023-05-15 17:22:13 +02:00
1b65ff5a65 Update zigbee-herdsman-converters to 15.0.113 (#17676)
* Update zigbee-herdsman-converters to 15.0.113

* Fix

---------

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2023-05-14 18:01:04 +02:00
github-actions[bot]andKoenkk e3ebb1c5bc Update dependencies (#17672)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-14 17:09:02 +02:00
github-actions[bot]andKoenkk 099a8d35b0 Update zigbee-herdsman-converters to 15.0.112 (#17660)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-12 18:32:50 +00:00
github-actions[bot]andKoenkk 3085608a35 Update zigbee-herdsman-converters to 15.0.111 (#17646)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-11 18:29:49 +00:00
github-actions[bot]andKoenkk 307e0dcadd Update zigbee-herdsman-converters to 15.0.110 (#17631)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-10 18:08:42 +00:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> aeb737012c Bump alpine from 3.17.3 to 3.18.0 in /docker (#17630)
Bumps alpine from 3.17.3 to 3.18.0.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-10 19:43:37 +02:00
github-actions[bot]andKoenkk dddd2bca88 Update zigbee-herdsman-converters to 15.0.109 (#17620)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-09 17:51:07 +00:00
Koen Kanters daedc54dd6 Update wrong_device.yaml 2023-05-09 19:30:18 +02:00
Koen Kanters df80a11663 Fix incorrect message being published when entity contains light or cover. https://github.com/Koenkk/zigbee2mqtt/issues/17594 2023-05-09 19:26:46 +02:00
github-actions[bot]andKoenkk a6bebf1e7a Update zigbee-herdsman-converters to 15.0.108 (#17603)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-08 19:11:34 +00:00
github-actions[bot]andKoenkk 52fdbbe057 Update zigbee-herdsman to 0.14.115 (#17602)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-08 20:27:28 +02:00
github-actions[bot]andKoenkk b2422589dc Update dependencies (#17581)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-08 20:21:24 +02:00
github-actions[bot]andKoenkk d602426951 Update zigbee-herdsman-converters to 15.0.106 (#17589)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-07 17:13:43 +00:00
Koen Kanters 64009d0dbb Fix Home Assistant State max length is 255 characters error for program sensor. https://github.com/Koenkk/zigbee2mqtt/issues/16460 2023-05-07 09:18:58 +02:00
github-actions[bot]andnurikk b8e46075df Update zigbee2mqtt-frontend to 0.6.129 (#17579)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2023-05-06 14:50:12 +00:00
github-actions[bot]andKoenkk 589d656594 Update zigbee-herdsman-converters to 15.0.105 (#17577)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-06 14:19:06 +00:00
github-actions[bot]andKoenkk 0872c670ad Update zigbee-herdsman to 0.14.114 (#17575)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-06 10:37:40 +00:00
github-actions[bot]andKoenkk 9b68684428 Update zigbee-herdsman-converters to 15.0.104 (#17573)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-05 18:29:10 +00:00
github-actions[bot]andKoenkk 626b8d2b08 Update zigbee-herdsman-converters to 15.0.103 (#17564)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-04 18:05:17 +00:00
github-actions[bot]andKoenkk 10d111e36a Update zigbee-herdsman-converters to 15.0.102 (#17555)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-03 18:29:59 +00:00
github-actions[bot]andKoenkk eb93c8aed7 Update zigbee-herdsman to 0.14.113 (#17554)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-03 18:18:56 +00:00
github-actions[bot]andKoenkk fe005e478a Update zigbee-herdsman-converters to 15.0.101 (#17543)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-02 17:56:22 +00:00
github-actions[bot]andKoenkk 8f88c4d26c Update zigbee-herdsman to 0.14.112 (#17523)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-05-01 17:14:11 +00:00
Koen Kanters 946887b841 Promote to dev 2023-05-01 18:41:46 +02:00
Koen Kanters b2dd21e164 1.30.4 2023-05-01 18:40:11 +02:00
Koen Kanters 01f4f1f940 Undev 2023-05-01 18:38:57 +02:00
Koen Kanters 563eb3a890 Merge branch 'dev' 2023-05-01 18:38:16 +02:00
github-actions[bot]andKoenkk ab4101dae8 Update zigbee-herdsman-converters to 15.0.100 (#17511)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-30 17:42:01 +00:00
9ab37735fe Update zigbee-herdsman-converters to 15.0.99 (#17503)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2023-04-30 16:30:36 +00:00
github-actions[bot]andKoenkk 8f275f414b Update zigbee-herdsman to 0.14.111 (#17504)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-30 07:32:09 +00:00
github-actions[bot]andKoenkk 55b17c95a4 Update dependencies (#17501)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-30 08:24:47 +02:00
github-actions[bot]andKoenkk 13c7348a97 Update zigbee-herdsman to 0.14.110 (#17486)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-28 17:58:44 +00:00
github-actions[bot]andKoenkk 62453a5d2a Update zigbee-herdsman-converters to 15.0.98 (#17476)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-27 17:38:14 +00:00
github-actions[bot]andKoenkk 9840433fc0 Update zigbee-herdsman-converters to 15.0.97 (#17470)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-27 07:27:05 +00:00
Aarni Koskela 8fe36d74ec Typo fixes (#17440)
* Fix "Succesfully"

* Fix herdsman typo

* Fix various other small typos
2023-04-27 09:14:04 +02:00
github-actions[bot]andKoenkk 8e85a1e081 Update zigbee-herdsman to 0.14.109 (#17469)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-27 07:12:42 +00:00
ce9a8ed86f Update zigbee-herdsman-converters to 15.0.96 (#17453)
* Update zigbee-herdsman-converters to 15.0.96

* Fix tests

---------

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2023-04-24 19:34:18 +00:00
github-actions[bot]andKoenkk 49a1f56641 Update zigbee-herdsman-converters to 15.0.95 (#17428)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-23 07:50:40 +00:00
github-actions[bot]andKoenkk 76cf6fa444 Update dependencies (#17427)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-23 08:36:46 +02:00
Koen Kanters e1ecbd97d0 Wrong device issue improvements 2023-04-22 20:47:08 +02:00
Koen Kanters c389ab28cd Add wrong device issue 2023-04-22 20:44:23 +02:00
Koen Kanters 4c0c69b15f Support Node.js 20 (#17422) 2023-04-22 16:53:15 +02:00
github-actions[bot]andKoenkk 704bbb54d3 Update zigbee-herdsman-converters to 15.0.94 (#17407)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-20 19:29:39 +00:00
github-actions[bot]andKoenkk 17947aa809 Update zigbee-herdsman-converters to 15.0.93 (#17386)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-18 17:42:08 +00:00
github-actions[bot]andKoenkk 33811fec9f Update zigbee-herdsman-converters to 15.0.92 (#17378)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-17 16:35:22 +00:00
github-actions[bot]andKoenkk 76cd07a701 Update zigbee-herdsman-converters to 15.0.91 (#17367)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-16 19:25:16 +00:00
github-actions[bot]andKoenkk e2b18d8f02 Update zigbee-herdsman to 0.14.108 (#17357)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-16 07:53:49 +00:00
b3c886638a Update dependencies (#17354)
* Update dependencies

* Fix tests

---------

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2023-04-16 08:56:31 +02:00
Sven Serlier f9121b2f0f Readme improvements (#17350)
* Update URLs

* Add more URLs
2023-04-15 19:06:56 +02:00
github-actions[bot]andKoenkk fb827f9dc0 Update zigbee-herdsman to 0.14.107 (#17351)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-15 17:06:40 +00:00
github-actions[bot]andKoenkk e5be5b0219 Update zigbee-herdsman-converters to 15.0.90 (#17343)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-15 08:04:45 +00:00
github-actions[bot]andKoenkk c360f5a7b1 Update zigbee-herdsman-converters to 15.0.89 (#17341)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-14 18:49:04 +00:00
Viktor Ryzhaiev 15f741b446 Add carbon_monoxide device_class to Home Assistant discovery (#17331) 2023-04-14 20:23:29 +02:00
github-actions[bot]andKoenkk 3b648c9e63 Update zigbee-herdsman to 0.14.106 (#17338)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-14 18:10:17 +00:00
github-actions[bot]andKoenkk 951360beba Update zigbee-herdsman-converters to 15.0.88 (#17324)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-13 17:20:22 +00:00
github-actions[bot]andKoenkk 71df01d613 Update zigbee-herdsman-converters to 15.0.87 (#17302)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-11 16:25:37 +00:00
github-actions[bot]andKoenkk a8f4daff30 Update zigbee-herdsman-converters to 15.0.86 (#17295)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-10 08:06:35 +00:00
github-actions[bot]andKoenkk d7dc083351 Update dependencies (#17286)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-09 09:06:03 +02:00
github-actions[bot]andKoenkk 45d55c9c71 Update zigbee-herdsman-converters to 15.0.85 (#17282)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-08 16:26:02 +00:00
085d5c9f8b Update zigbee-herdsman-converters to 15.0.84 (#17266)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2023-04-08 09:05:23 +00:00
github-actions[bot]andKoenkk 09a5650345 Update zigbee-herdsman to 0.14.105 (#17265)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-06 18:50:52 +00:00
github-actions[bot]andKoenkk 24050a3a10 Update zigbee-herdsman-converters to 15.0.83 (#17256)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-05 18:26:01 +00:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> c6a2400f56 Bump peter-evans/create-pull-request from 4 to 5 (#17255)
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4 to 5.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](https://github.com/peter-evans/create-pull-request/compare/v4...v5)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-05 19:58:23 +02:00
github-actions[bot]andKoenkk 9da7f53e78 Update zigbee-herdsman-converters to 15.0.82 (#17246)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-04 18:16:44 +00:00
github-actions[bot]andKoenkk f5a2c802ca Update zigbee-herdsman to 0.14.104 (#17245)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-04 18:13:41 +00:00
github-actions[bot]andKoenkk 372d991db9 Update zigbee-herdsman-converters to 15.0.81 (#17232)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-03 16:15:00 +00:00
github-actions[bot]andKoenkk bab0a3e902 Update zigbee-herdsman-converters to 15.0.80 (#17217)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-02 08:12:19 +00:00
github-actions[bot]andKoenkk b85aaf8e54 Update dependencies (#17215)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-02 09:24:23 +02:00
github-actions[bot]andKoenkk 64d744ea88 Update zigbee-herdsman-converters to 15.0.79 (#17203)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-01 07:31:16 +00:00
Koen Kanters 7748dd3321 Promote to dev 2023-04-01 09:06:17 +02:00
Koen Kanters 24c6b2e2c9 1.30.3 2023-04-01 09:05:40 +02:00
Koen Kanters 9589d12353 Undev 2023-04-01 09:05:26 +02:00
github-actions[bot]andKoenkk dcc13b7b53 Update zigbee-herdsman-converters to 15.0.78 (#17201)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-04-01 06:51:48 +00:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 2d1733d0e7 Bump alpine from 3.17.2 to 3.17.3 in /docker (#17189)
Bumps alpine from 3.17.2 to 3.17.3.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-30 18:41:09 +02:00
github-actions[bot]andKoenkk 1368a78767 Update zigbee-herdsman-converters to 15.0.77 (#17174)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-29 07:05:47 +00:00
github-actions[bot]andKoenkk f29073e413 Update zigbee-herdsman to 0.14.103 (#17173)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-29 06:59:49 +00:00
github-actions[bot]andKoenkk 7005497cba Update zigbee-herdsman-converters to 15.0.76 (#17164)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-28 12:03:17 +00:00
github-actions[bot]andKoenkk 298c595deb Update zigbee-herdsman to 0.14.102 (#17163)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-28 11:52:39 +00:00
github-actions[bot]andKoenkk bdbe2b4a70 Update zigbee-herdsman to 0.14.101 (#17159)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-27 14:40:49 +00:00
Thomas KintscherandThomas Kintscher 653334b208 Expose voc_index sensor measurements as such in Home Assistant (#17144)
Co-authored-by: Thomas Kintscher <mail@thomaskintscher.de>
2023-03-26 09:50:22 +02:00
github-actions[bot]andKoenkk 24c6c0bf8e Update dependencies (#17146)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-26 09:03:01 +02:00
github-actions[bot]andKoenkk 3e252e4307 Update zigbee-herdsman-converters to 15.0.75 (#17127)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-23 16:26:30 +00:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ae2de04251 Bump actions/stale from 7 to 8 (#17126)
Bumps [actions/stale](https://github.com/actions/stale) from 7 to 8.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-23 16:22:08 +00:00
oddlama 07d238d5b9 Support hosting the frontend on a Unix socket (#17123) 2023-03-23 17:08:23 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 407f0df6ad Bump peter-evans/enable-pull-request-automerge from 2 to 3 (#17125)
Bumps [peter-evans/enable-pull-request-automerge](https://github.com/peter-evans/enable-pull-request-automerge) from 2 to 3.
- [Release notes](https://github.com/peter-evans/enable-pull-request-automerge/releases)
- [Commits](https://github.com/peter-evans/enable-pull-request-automerge/compare/v2...v3)

---
updated-dependencies:
- dependency-name: peter-evans/enable-pull-request-automerge
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-23 17:06:11 +01:00
github-actions[bot]andKoenkk 2e593e37a6 Update zigbee-herdsman-converters to 15.0.74 (#17102)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-21 20:07:52 +00:00
github-actions[bot]andKoenkk 9f7fd479c4 Update zigbee-herdsman-converters to 15.0.73 (#17090)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-20 17:20:59 +00:00
github-actions[bot]andnurikk 0407a3b67c Update zigbee2mqtt-frontend to 0.6.127 (#17086)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2023-03-19 23:38:39 +00:00
github-actions[bot]andKoenkk 2c57bf81b0 Update zigbee-herdsman to 0.14.100 (#17078)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-19 08:13:42 +00:00
github-actions[bot]andKoenkk 960456401e Update dependencies (#17075)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-19 09:02:46 +01:00
github-actions[bot]andKoenkk d4774526c3 Update zigbee-herdsman-converters to 15.0.72 (#17073)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-18 16:13:01 +00:00
github-actions[bot]andKoenkk 2a365d39ac Update zigbee-herdsman-converters to 15.0.71 (#17055)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-16 16:46:28 +00:00
github-actions[bot]andKoenkk 347c023968 Update zigbee-herdsman-converters to 15.0.70 (#17052)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-15 16:38:15 +00:00
github-actions[bot]andKoenkk 4272d9a13e Update zigbee-herdsman-converters to 15.0.69 (#17028)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-13 17:16:42 +00:00
github-actions[bot]andKoenkk 54900c3680 Update zigbee-herdsman to 0.14.99 (#17026)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-13 16:44:47 +00:00
github-actions[bot]andKoenkk 461519cf39 Update zigbee-herdsman-converters to 15.0.68 (#17013)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-12 12:41:25 +00:00
github-actions[bot]andKoenkk 8edb176dc0 Update dependencies (#17007)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-12 13:00:09 +01:00
github-actions[bot]andKoenkk 61cdda96ff Update zigbee-herdsman-converters to 15.0.67 (#16994)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-11 10:10:41 +00:00
github-actions[bot]andKoenkk 43394b3376 Update zigbee-herdsman-converters to 15.0.66 (#16977)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-09 17:27:45 +00:00
github-actions[bot]andKoenkk ca7f9ec8ff Update zigbee-herdsman-converters to 15.0.65 (#16964)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-08 16:12:07 +00:00
github-actions[bot]andKoenkk 0857d087bd Update zigbee-herdsman-converters to 15.0.64 (#16954)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-07 19:19:45 +00:00
github-actions[bot]andKoenkk 818a9e3946 Update zigbee-herdsman to 0.14.98 (#16953)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-07 19:08:24 +00:00
github-actions[bot]andKoenkk 51460aacc8 Update zigbee-herdsman-converters to 15.0.63 (#16943)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-06 18:54:33 +00:00
github-actions[bot]andnurikk 354beb486d Update zigbee2mqtt-frontend to 0.6.126 (#16933)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2023-03-06 09:44:41 +00:00
github-actions[bot]andKoenkk 8261d4efc6 Update zigbee-herdsman-converters to 15.0.62 (#16921)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-05 08:59:13 +00:00
Koen Kanters bbb19371f3 Fix Home Assistant unit of measurement error for voc ppb sensors. https://github.com/Koenkk/zigbee2mqtt/issues/16057 2023-03-05 09:50:23 +01:00
github-actions[bot]andKoenkk 04ae93f00c Update dependencies (#16919)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-05 09:09:16 +01:00
github-actions[bot]andKoenkk 999c01086e Update zigbee-herdsman-converters to 15.0.61 (#16915)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-04 16:09:08 +00:00
baze-andKoen Kanters 0e67f8fa0a Fix old state send when long debounce time is used (#16828)
* Fixed caching problem where old messages were sent out if long debounce values is used for sensor.

* Fixed caching problem where old messages were sent out if long debounce values is used for sensor

* State cache updated during debounce IF cache is enabled in configuration.

* Fixed code styles

* Implemented test case for testing State cache is updated correctly during debouncing

* If-statement was obsolete as cache can be update right away.

---------

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2023-03-04 16:43:01 +01:00
github-actions[bot]andKoenkk f352d18854 Update zigbee-herdsman to 0.14.97 (#16914)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-04 15:17:35 +00:00
github-actions[bot]andKoenkk f865c976e3 Update zigbee-herdsman-converters to 15.0.60 (#16885)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-02 19:03:57 +00:00
github-actions[bot]andKoenkk aa90765db8 Update zigbee-herdsman-converters to 15.0.59 (#16862)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-01 16:18:51 +00:00
Koen Kanters 017fc03157 Promote to dev 2023-03-01 16:44:55 +01:00
Koen Kanters cdf62ea560 1.30.2 2023-03-01 16:43:20 +01:00
Koen Kanters 5dcd3f67ed Undev 2023-03-01 16:43:06 +01:00
github-actions[bot]andKoenkk b013a7d8ef Update zigbee-herdsman-converters to 15.0.58 (#16860)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-03-01 15:28:46 +00:00
github-actions[bot]andKoenkk 8e6e38b968 Update zigbee-herdsman to 0.14.96 (#16851)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-28 16:00:34 +00:00
github-actions[bot]andKoenkk 8f20640008 Update zigbee-herdsman-converters to 15.0.57 (#16850)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-28 15:51:18 +00:00
github-actions[bot]andnurikk 57707cd9b9 Update zigbee2mqtt-frontend to 0.6.125 (#16844)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2023-02-27 22:28:55 +00:00
github-actions[bot]andKoenkk 911683ad13 Update zigbee-herdsman-converters to 15.0.56 (#16841)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-27 19:42:13 +00:00
github-actions[bot]andKoenkk 99204682b8 Update dependencies (#16819)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-26 08:57:31 +01:00
github-actions[bot]andKoenkk fec7fca89e Update zigbee-herdsman-converters to 15.0.55 (#16814)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-25 10:17:03 +00:00
github-actions[bot]andKoenkk 6bea88eeb6 Update zigbee-herdsman to 0.14.95 (#16813)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-25 10:09:26 +00:00
github-actions[bot]andKoenkk 6afcb97c0d Update zigbee-herdsman-converters to 15.0.54 (#16798)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-23 17:09:09 +00:00
github-actions[bot]andKoenkk 73d60396bc Update zigbee-herdsman-converters to 15.0.53 (#16788)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-22 20:09:37 +00:00
Koen Kanters 942faf412f Revert "Capitalize entity name in Home Assistant (#16702)"
This reverts commit f5cceb1719.
2023-02-22 20:34:39 +01:00
github-actions[bot]andKoenkk b62f65f045 Update zigbee-herdsman-converters to 15.0.52 (#16771)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-21 16:26:31 +00:00
Artem Draft f5cceb1719 Capitalize entity name in Home Assistant (#16702) 2023-02-21 17:18:15 +01:00
github-actions[bot]andKoenkk 0b6040dd25 Update zigbee-herdsman-converters to 15.0.51 (#16765)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-20 19:32:31 +00:00
github-actions[bot]andKoenkk 45fda8aefd Update zigbee-herdsman to 0.14.94 (#16764)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-20 19:14:42 +00:00
Andreas Fritiofson 85f32c039f Add entity_category to indicator_mode and led_enable for Home Assistant discovery (#16744)
These names are used by some TuYa and IKEA devices.
2023-02-20 19:53:22 +01:00
github-actions[bot]andKoenkk 7fa2b5fcc3 Update zigbee-herdsman-converters to 15.0.50 (#16742)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-19 09:32:01 +00:00
github-actions[bot]andKoenkk 08a0ace11a Update dependencies (#16738)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-19 09:36:47 +01:00
github-actions[bot]andKoenkk 7605e1ec06 Update zigbee-herdsman-converters to 15.0.49 (#16722)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-18 08:53:16 +00:00
Koen Kanters e934b0d94e Support Hue effects for groups. https://github.com/Koenkk/zigbee2mqtt/issues/15997 2023-02-16 18:20:08 +01:00
github-actions[bot]andKoenkk 4aa8e17214 Update zigbee-herdsman-converters to 15.0.48 (#16694)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-15 19:36:12 +00:00
github-actions[bot]andKoenkk a9f5f7c620 Update zigbee-herdsman-converters to 15.0.47 (#16679)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-14 19:11:35 +00:00
github-actions[bot]andKoenkk 6e193ca442 Update zigbee-herdsman-converters to 15.0.46 (#16661)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-13 18:48:16 +00:00
Tami 2eadf30199 Fix error when device is added when device: [] in configuration.yaml (#16629)
* fix for TypeError: The "path" argument must be of type string in writeDevicesOrGroups

* added test case that produces the error when running on old implementation

Summary of all failing tests
 FAIL  test/settings.test.js
  ● Settings › Should add devices even when devices exist empty

    TypeError: The "path" argument must be of type string. Received undefined

      275 |
      276 | jest.mock('../../lib/util/data', () => ({
    > 277 |     joinPath: (file) => require('path').join(mockDir, file),
          |                                         ^
      278 |     getPath: () => mockDir,
      279 | }));
      280 |

      at Object.join [as joinPath] (test/stub/data.js:277:41)
      at joinPath (lib/util/settings.ts:245:38)
      at writeDevicesOrGroups (lib/util/settings.ts:250:5)
      at Object.write [as addDevice] (lib/util/settings.ts:555:5)
      at Object.addDevice (test/settings.test.js:128:18)
2023-02-13 19:41:49 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 600e743941 Bump alpine from 3.17.1 to 3.17.2 in /docker (#16659)
Bumps alpine from 3.17.1 to 3.17.2.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-13 19:23:38 +01:00
github-actions[bot]andKoenkk 932541649e Update zigbee-herdsman to 0.14.93 (#16635)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-12 08:44:39 +00:00
Artem Draft 0217593c77 Improve checks in HA covers (#16614) 2023-02-12 09:28:57 +01:00
github-actions[bot]andKoenkk 10e65b5f26 Update dependencies (#16632)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-12 09:17:14 +01:00
github-actions[bot]andKoenkk e9a38353fa Update zigbee-herdsman to 0.14.92 (#16616)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-10 20:42:59 +00:00
68caf0260d Update zigbee-herdsman-converters to 15.0.45 (#16604)
* Update zigbee-herdsman-converters to 15.0.45

* Fix tests

---------

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2023-02-09 18:32:04 +01:00
github-actions[bot]andKoenkk 7d887adb7f Update zigbee-herdsman to 0.14.91 (#16603)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-09 18:04:15 +01:00
github-actions[bot]andKoenkk ec57d899e5 Update zigbee-herdsman-converters to 15.0.44 (#16576)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-07 19:41:16 +00:00
github-actions[bot]andKoenkk 61b1732ecb Update zigbee-herdsman-converters to 15.0.43 (#16558)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-06 16:58:21 +00:00
Koen Kanters a35705cd38 Merge branch 'master' into dev 2023-02-06 17:49:55 +01:00
Koen Kanters eb878d3d8e 1.30.1 2023-02-06 17:42:06 +01:00
Robert Kaussow 3420cf508b disable buildx provenance in the ci build command (#16495) 2023-02-06 17:39:51 +01:00
Koen Kanters d5a3bf626f Update zigbee-herdsman-converters to 15.0.38-hotfix.0 2023-02-06 17:39:10 +01:00
github-actions[bot]andKoenkk 0e1220ff24 Update zigbee-herdsman-converters to 15.0.42 (#16542)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-05 14:54:53 +00:00
github-actions[bot]andKoenkk 1496b95067 Update dependencies (#16534)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-05 13:29:02 +01:00
github-actions[bot]andKoenkk bd0694e164 Update zigbee-herdsman to 0.14.90 (#16523)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-04 14:23:17 +00:00
github-actions[bot]andKoenkk d580741dfe Update zigbee-herdsman-converters to 15.0.41 (#16522)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-04 14:11:41 +00:00
Robert Kaussow a24586bec3 disable buildx provenance in the ci build command (#16495) 2023-02-04 14:12:24 +01:00
github-actions[bot]andKoenkk 1e7a76139d Update zigbee-herdsman-converters to 15.0.40 (#16487)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-02 16:57:11 +00:00
github-actions[bot]andKoenkk ea0a9990b9 Update zigbee-herdsman-converters to 15.0.39 (#16459)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-01 17:20:38 +00:00
Koen Kanters 97af5a64e7 Promote to dev 2023-02-01 17:17:11 +01:00
Koen Kanters 8f781db93e 1.30.0 2023-02-01 17:14:40 +01:00
Koen Kanters 1a6ba18ec2 Undev 2023-02-01 17:14:22 +01:00
Koen Kanters d91e363d76 Merge branch 'dev' 2023-02-01 17:13:53 +01:00
github-actions[bot]andKoenkk f7e45b8284 Update zigbee-herdsman-converters to 15.0.38 (#16452)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-02-01 14:58:34 +00:00
github-actions[bot]andKoenkk bbbaaf4d89 Update zigbee-herdsman-converters to 15.0.37 (#16443)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-31 17:17:49 +00:00
github-actions[bot]andKoenkk c9076f8ce6 Update zigbee-herdsman to 0.14.89 (#16442)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-31 18:11:43 +01:00
github-actions[bot]andKoenkk 82fd062668 Update zigbee-herdsman-converters to 15.0.36 (#16429)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-30 17:22:52 +00:00
github-actions[bot]andKoenkk 769a1afd96 Update zigbee-herdsman-converters to 15.0.35 (#16416)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-29 18:28:16 +00:00
Koen Kanters f943619387 Always respond to OTA request. https://github.com/Koenkk/zigbee2mqtt/issues/16371 2023-01-29 19:16:03 +01:00
github-actions[bot]andKoenkk 43e204b7d1 Update zigbee-herdsman-converters to 15.0.34 (#16409)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-29 11:55:41 +00:00
Koen Kanters a3f1399489 Log device OTA request response. https://github.com/Koenkk/zigbee2mqtt/issues/16371 2023-01-29 11:23:21 +01:00
github-actions[bot]andKoenkk 4179d879d2 Update dependencies (#16400)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-29 10:27:14 +01:00
github-actions[bot]andKoenkk 6ce2d2e73f Update zigbee-herdsman-converters to 15.0.33 (#16374)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-26 16:54:37 +00:00
github-actions[bot]andKoenkk 981542827c Update zigbee-herdsman-converters to 15.0.32 (#16355)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-25 18:24:35 +00:00
github-actions[bot]andKoenkk 2f0a2cee6e Update zigbee-herdsman-converters to 15.0.31 (#16335)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-24 16:30:10 +00:00
Koen Kanters 0bc685a8e3 Sanatize manufacturerName. https://github.com/home-assistant/core/issues/85691 2023-01-24 17:19:56 +01:00
Artem Draft 02a99703a8 Fix motor_state and moving in HA covers (#16324) 2023-01-24 17:00:24 +01:00
github-actions[bot]andKoenkk 7639ce44a7 Update zigbee-herdsman-converters to 15.0.30 (#16322)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-23 19:26:59 +00:00
Koen Kanters b89a99fe0c Fix another Failed to call 'Frontend' 'stop' . https://github.com/Koenkk/zigbee2mqtt/issues/16317 2023-01-23 20:21:03 +01:00
mtdcr 06820d2220 Sane defaults for remote syslog (#16300)
The most commonly used transport for remote syslog is port 514/udp.
It also is the only defined method for BSD syslog (RFC 3164). RFCs
5424+5425 specify an additional TLS transport via 6514/tcp, which
does not seem to be supported by zigbee2mqtt, as evidenced by the
lack of TLS-related configuration options in this area. RFC 6587
describes legacy uses of syslog over TCP, recommending against them.

Port 123, the previous default, was probably used by accident and
is reserved for NTP.

| $ grep -w -e 123 -e 514 -e 601 -e 6514 /etc/services
| ntp		123/udp				# Network Time Protocol
| shell		514/tcp		cmd syslog	# no passwords used
| syslog	514/udp
| syslog-tls	6514/tcp			# Syslog over TLS [RFC5425]
2023-01-23 19:31:10 +01:00
Roland Ramthun 140213950d Disable provenance information in Docker images (#16297)
* Disable provenance information in Docker images

The new default of buildx to include provenance information to OCI images causes a series of infrastructure issues with current Docker versions. 

Disable generation of the new provenance information until all necessary infrastructure software components and registries have been fixed to support it properly.

* Correct docker/setup-buildx-action version number
2023-01-23 19:26:43 +01:00
github-actions[bot]andKoenkk f4f2c498b7 Update zigbee-herdsman-converters to 15.0.29 (#16295)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-22 08:45:14 +00:00
github-actions[bot]andKoenkk 31807c6c6b Update zigbee-herdsman to 0.14.88 (#16294)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-22 08:08:45 +00:00
github-actions[bot]andKoenkk 8ccad8ee53 Update dependencies (#16293)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-22 09:03:03 +01:00
github-actions[bot]andKoenkk a930539dc3 Update zigbee-herdsman-converters to 15.0.28 (#16284)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-21 09:37:42 +00:00
github-actions[bot]andnurikk b33db9397d Update zigbee2mqtt-frontend to 0.6.124 (#16277)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2023-01-20 15:19:13 +00:00
github-actions[bot]andKoenkk de4b7fb022 Update zigbee-herdsman-converters to 15.0.27 (#16264)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-19 16:58:25 +00:00
github-actions[bot]andKoenkk 9e85419f9e Update zigbee-herdsman-converters to 15.0.26 (#16254)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-18 20:28:00 +00:00
github-actions[bot]andKoenkk 80c464c951 Update zigbee-herdsman-converters to 15.0.25 (#16230)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-17 16:29:30 +00:00
github-actions[bot]andKoenkk e56dff4972 Update zigbee-herdsman-converters to 15.0.24 (#16215)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-16 18:46:18 +00:00
Koen Kanters d8501f04eb Fix Failed to call 'Frontend' 'stop' (TypeError: Cannot read properties of null (reading 'clients') error. https://github.com/Koenkk/zigbee2mqtt/issues/16207 2023-01-16 19:19:51 +01:00
github-actions[bot]andKoenkk c927b4b935 Update zigbee-herdsman-converters to 15.0.23 (#16177)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-15 13:26:50 +00:00
github-actions[bot]andKoenkk fb9872a4ff Update dependencies (#16172)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-15 10:05:39 +01:00
39b0964739 Update zigbee-herdsman-converters to 15.0.22 (#16159)
* Update zigbee-herdsman-converters to 15.0.22

* Fix tests

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2023-01-14 13:39:45 +00:00
Koen Kanters 2c509c0368 Promote to dev 2023-01-13 22:33:21 +01:00
Koen Kanters 49e1c68cac Merge branch 'master' into dev 2023-01-13 22:32:50 +01:00
Koen Kanters bb3e8f6d41 1.29.2 2023-01-13 22:28:52 +01:00
Koen Kanters 835a3288a0 Update zh to 0.14.83-hotfix.0 and zhc to 15.0.12-hotfix.1 2023-01-13 22:27:36 +01:00
Koen Kanters cddbb9a5e5 Fix f600066134 2023-01-13 16:59:20 +01:00
github-actions[bot]andnurikk 65366478e3 Update zigbee2mqtt-frontend to 0.6.123 (#16143)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2023-01-13 11:33:49 +00:00
1d8b19dce9 Update zigbee-herdsman-converters to 15.0.21 (#16123)
* Update zigbee-herdsman-converters to 15.0.21

* Fix tests

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2023-01-12 17:34:49 +00:00
Koen Kanters f600066134 Fix 328b9fbd60. https://github.com/Koenkk/zigbee2mqtt/issues/15958 2023-01-12 17:20:07 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 75533c20b8 Bump alpine from 3.17.0 to 3.17.1 in /docker (#16081)
Bumps alpine from 3.17.0 to 3.17.1.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-12 09:42:42 +01:00
Koen Kanters 0605a5e0e3 Attempt to improve Log when MQTT client is unavailable stability 2023-01-12 09:41:30 +01:00
cf9640bd59 Update zigbee-herdsman-converters to 15.0.20 (#16106)
* Update zigbee-herdsman-converters to 15.0.20

* Fix tests

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2023-01-12 08:37:17 +00:00
Koen Kanters 328b9fbd60 Remove state_class measurement when unit_of_measurement is not set. https://github.com/Koenkk/zigbee2mqtt/issues/15958 2023-01-11 18:23:13 +01:00
github-actions[bot]andnurikk bc6d1070c7 Update zigbee2mqtt-frontend to 0.6.122 (#16092)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2023-01-11 09:29:08 +00:00
github-actions[bot]andKoenkk bf339636b5 Update zigbee-herdsman to 0.14.87 (#16068)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-09 21:50:14 +00:00
github-actions[bot]andKoenkk d90dae5d9d Update zigbee-herdsman to 0.14.86 (#16066)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-09 21:00:50 +01:00
github-actions[bot]andKoenkk b34b0e866f Update zigbee-herdsman-converters to 15.0.19 (#16062)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-09 18:57:47 +00:00
Dan Klaffenbach 2288c0f1c2 Poll Busch-Jaeger devices for updates after binding-initiated changes (#16036)
The Busch-Jaeger devices don't support reporting and therefore their
state should be polled after being changed through a binding partner.
2023-01-09 19:18:13 +01:00
Miguel Cabral 9c4abb64c3 Expose cover moving state to Home Assistant (#16017) 2023-01-08 10:05:29 +01:00
github-actions[bot]andKoenkk 559f235e88 Update zigbee-herdsman-converters to 15.0.18 (#16023)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-08 09:01:56 +00:00
github-actions[bot]andKoenkk 30df0e25f9 Update zigbee-herdsman to 0.14.85 (#16020)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-08 08:44:31 +00:00
Koen Kanters e7735112e8 Fix Home Assistant Entity sensor.xxx (<class 'homeassistant.components.mqtt.sensor.MqttSensor'>) is using native unit of measurement '' which is not a valid unit for the device class ('illuminance') warning. https://github.com/Koenkk/zigbee2mqtt/issues/15958 2023-01-08 08:50:10 +01:00
github-actions[bot]andKoenkk b943feef29 Update dependencies (#16019)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-08 08:45:04 +01:00
Koen Kanters e92d3f4ede Promote to dev 2023-01-07 10:26:02 +01:00
Koen Kanters 044cd0b3a6 Merge branch 'master' into dev 2023-01-07 10:25:19 +01:00
github-actions[bot]andKoenkk 3351bc2f42 Update zigbee-herdsman to 0.14.84 (#15978)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-06 11:29:07 +01:00
github-actions[bot]andKoenkk 6a8d236b4d Update zigbee-herdsman-converters to 15.0.17 (#15977)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-06 10:26:52 +00:00
Koen Kanters 7d67ffc429 1.29.1 2023-01-06 11:07:47 +01:00
Koen Kanters f984734961 Update zigbee-herdsman-converters to v15.0.12-hotfix.0 2023-01-06 11:07:14 +01:00
Koen Kanters 217fbb284a Fix Home Assistant Template variable warning: 'dict object' has no attribute 'latest_version' when rendering '{{ value_json['update']['latest_version'] }}' error attempt 2. https://github.com/Koenkk/zigbee2mqtt/issues/15747 2023-01-06 10:41:43 +01:00
Koen Kanters 9fe48e2a88 Revert "Expose cover moving state to Home Assistant (#15555)"
This reverts commit 56f7f3e1bd.
2023-01-06 10:41:37 +01:00
Koen Kanters 1c7817ad77 Fix Home Assistant Template variable warning: 'dict object' has no attribute 'latest_version' when rendering '{{ value_json['update']['latest_version'] }}' error. https://github.com/Koenkk/zigbee2mqtt/issues/15747 2023-01-06 10:41:13 +01:00
github-actions[bot]andKoenkk cf801344f8 Update zigbee-herdsman-converters to 15.0.16 (#15947)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-04 19:00:11 +00:00
Koen Kanters 50bca161ce Fix Home Assistant Template variable warning: 'dict object' has no attribute 'latest_version' when rendering '{{ value_json['update']['latest_version'] }}' error attempt 2. https://github.com/Koenkk/zigbee2mqtt/issues/15747 2023-01-04 19:49:27 +01:00
github-actions[bot]andKoenkk 15ccf945c3 Update zigbee-herdsman-converters to 15.0.15 (#15906)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-03 10:27:14 +00:00
Koen Kanters d49f06f34d Revert "Expose cover moving state to Home Assistant (#15555)"
This reverts commit 56f7f3e1bd.
2023-01-03 10:59:20 +01:00
github-actions[bot]andKoenkk 826f03220d Update zigbee-herdsman-converters to 15.0.14 (#15876)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-02 09:12:10 +00:00
Koen Kanters cdb984f830 Fix Home Assistant Template variable warning: 'dict object' has no attribute 'latest_version' when rendering '{{ value_json['update']['latest_version'] }}' error. https://github.com/Koenkk/zigbee2mqtt/issues/15747 2023-01-02 09:18:36 +01:00
github-actions[bot]andKoenkk 4e40c8223f Update zigbee-herdsman-converters to 15.0.13 (#15852)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-01 10:27:09 +00:00
github-actions[bot]andKoenkk 02d86ab233 Update dependencies (#15848)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-01-01 11:13:54 +01:00
Koen Kanters a49079f560 Dev 2023-01-01 10:58:37 +01:00
Koen Kanters dddce571d3 1.29.0 2023-01-01 10:53:46 +01:00
Koen Kanters f1841377db Undev 2023-01-01 10:52:49 +01:00
Koen Kanters f3edfbf75b Update generateNewSupportedDevices.js 2023-01-01 10:52:02 +01:00
github-actions[bot]andnurikk d701d96bb2 Update zigbee2mqtt-frontend to 0.6.121 (#15845)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-12-31 19:11:30 +00:00
github-actions[bot]andKoenkk 3b9d6c5651 Update zigbee-herdsman-converters to 15.0.12 (#15837)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-31 09:36:55 +00:00
MattandKoen Kanters 19bd87de4a Keep cached device state until Zigbee2MQTT is stopped (#15770)
* Fix extension path to make debugginge easier in VScode

* Fix linting error

* Add setting "cache_state_persist_on_leave" (default false)

This setting does not remove a device from State if it leaves the network

* Merge from upstream kk/dev

* Re-define cache_state_persist_on_leave to be a timeout

Allows the config to specify how long the state should be held (and the device to re-join & maintain the state).

Remove all pending deletions if the system is stopped

* Add debug. Fix "time" test

* Disable esline warnings for long debug lines

* Simplify state maintenance for leave/join devices

* Restore default configuration.yaml

* Don't track device presence via events, but examine herdsman on system stop

* Update configuration.yaml

* Update state.ts

* Update state.ts

* Update controller.test.js

* Update eventBus.ts

* Update eventBus.ts

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-12-31 10:15:11 +01:00
github-actions[bot]andKoenkk af27e910d8 Update zigbee-herdsman-converters to 15.0.11 (#15816)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-30 08:22:39 +00:00
github-actions[bot]andKoenkk d8f1f3ef06 Update zigbee-herdsman-converters to 15.0.10 (#15797)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-29 09:50:01 +00:00
Koen Kanters 5b2756fef3 Build linux/386 Docker image. https://github.com/Koenkk/zigbee2mqtt/issues/15796 2022-12-29 10:44:15 +01:00
github-actions[bot]andKoenkk 75db8cf3f1 Update zigbee-herdsman-converters to 15.0.9 (#15775)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-28 08:28:49 +00:00
github-actions[bot]andKoenkk b4660417f8 Update zigbee-herdsman-converters to 15.0.8 (#15764)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-27 08:28:19 +00:00
github-actions[bot]andKoenkk 75e677608b Update zigbee-herdsman-converters to 15.0.7 (#15757)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-26 11:43:44 +00:00
Matt 05cb1406bb Fix extension path to make debugging easier in VS Code (#15741)
* Fix extension path to make debugginge easier in VScode

* Fix linting error
2022-12-25 16:57:48 +01:00
github-actions[bot]andKoenkk 10c6d1e4d9 Update dependencies (#15746)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-25 16:40:03 +01:00
Koen Kanters 6d08a2f0f6 Use Node.js 18 in CI 2022-12-24 09:50:32 +01:00
Koen Kanters 8d8c46ecae Reduce Docker image size 2022-12-24 09:45:15 +01:00
Koen Kanters 1f642d86e8 Add disabled option (#15589)
* Add disabled option

* Updates

* Tests

* Add restart required
2022-12-23 23:56:29 +01:00
github-actions[bot]andKoenkk 5afda96cf4 Update zigbee-herdsman-converters to 15.0.6 (#15722)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-23 19:39:13 +00:00
github-actions[bot]andnurikk 2550ca5978 Update zigbee2mqtt-frontend to 0.6.120 (#15717)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-12-23 16:55:52 +00:00
github-actions[bot]andKoenkk 566fc625cd Update zigbee-herdsman-converters to 15.0.4 (#15703)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-22 17:24:59 +00:00
github-actions[bot]andKoenkk 7f9de55b6c Update zigbee-herdsman-converters to 15.0.3 (#15689)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-21 19:18:48 +00:00
github-actions[bot]andKoenkk ccb5b49c80 Update zigbee-herdsman to 0.14.83 (#15688)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-21 19:13:08 +00:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> dea44579c3 Bump actions/stale from 6 to 7 (#15685)
Bumps [actions/stale](https://github.com/actions/stale) from 6 to 7.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-21 17:03:55 +01:00
github-actions[bot]andKoenkk 58445a2327 Update zigbee-herdsman to 0.14.82 (#15673)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-20 22:09:08 +00:00
github-actions[bot]andKoenkk 98cf8dbe5e Update zigbee-herdsman-converters to 15.0.2 (#15671)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-20 21:55:29 +00:00
github-actions[bot]andKoenkk 8fc83caac7 Update zigbee-herdsman-converters to 15.0.1 (#15651)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-19 20:07:35 +00:00
Miguel Cabral 56f7f3e1bd Expose cover moving state to Home Assistant (#15555) 2022-12-19 20:27:15 +01:00
Koen Kanters 01b46cf74e Use fileVersion for Home Assistant update sensor instead of softwareBuildID (#15630)
* Use fileVersion for Home Assistant update sensor instead of softwareBuildID. https://github.com/Koenkk/zigbee2mqtt/issues/15502

* Remove onlythis
2022-12-18 22:05:16 +01:00
github-actions[bot]andKoenkk 825c761c26 Update zigbee-herdsman-converters to 15.0.0 (#15631)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-18 21:04:48 +00:00
github-actions[bot]andKoenkk 443eb23a91 Update zigbee-herdsman-converters to 14.0.694 (#15624)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-18 13:01:34 +00:00
github-actions[bot]andKoenkk 9b78d85a46 Update dependencies (#15618)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-18 13:04:08 +01:00
6731c85981 Update zigbee-herdsman-converters to 14.0.693 (#15603)
* Update zigbee-herdsman-converters to 14.0.693

* fix tests

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-12-17 10:04:38 +00:00
github-actions[bot]andKoenkk da44667926 Update zigbee-herdsman-converters to 14.0.692 (#15573)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-15 20:02:58 +00:00
github-actions[bot]andKoenkk 3796084a27 Update zigbee-herdsman-converters to 14.0.691 (#15538)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-13 20:16:17 +00:00
Koen Kanters 4d32326a4f Fix failing configure blocking startup. https://github.com/Koenkk/zigbee2mqtt/issues/15299 2022-12-12 21:35:10 +01:00
github-actions[bot]andKoenkk 54d8002fd3 Update zigbee-herdsman-converters to 14.0.690 (#15517)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-12 20:31:55 +00:00
github-actions[bot]andKoenkk 21a30fc555 Update zigbee-herdsman-converters to 14.0.689 (#15490)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-11 09:44:07 +00:00
github-actions[bot]andKoenkk b90c0c4114 Update zigbee-herdsman to 0.14.81 (#15489)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-11 10:37:37 +01:00
github-actions[bot]andKoenkk e2deb23d77 Update dependencies (#15484)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-11 09:07:13 +01:00
github-actions[bot]andKoenkk b69d31d3ee Update zigbee-herdsman-converters to 14.0.688 (#15480)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-10 15:39:17 +00:00
github-actions[bot]andKoenkk 3481423810 Update zigbee-herdsman-converters to 14.0.687 (#15465)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-09 17:09:17 +00:00
github-actions[bot]andKoenkk d093200fec Update zigbee-herdsman to 0.14.80 (#15463)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-09 16:38:33 +00:00
eaf834a921 Update zigbee-herdsman to 0.14.79 (#15449)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-12-08 19:48:45 +01:00
github-actions[bot]andKoenkk 27377b0658 Update zigbee-herdsman-converters to 14.0.686 (#15448)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-08 18:40:40 +00:00
github-actions[bot]andKoenkk 2c53bc2c61 Update zigbee-herdsman-converters to 14.0.685 (#15430)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-07 19:15:23 +00:00
Gustav WestlingandKoen Kanters 83fae81724 docker: rebuild serialport from source (#15400)
* docker: rebuild serialport from source

* Update Dockerfile

* Update Dockerfile

* Update Dockerfile

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-12-07 17:54:24 +01:00
github-actions[bot]andKoenkk d075f0752e Update zigbee-herdsman-converters to 14.0.684 (#15399)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-06 17:21:51 +00:00
github-actions[bot]andKoenkk a29f90b441 Update zigbee-herdsman to 0.14.78 (#15398)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-06 17:11:48 +00:00
Koen Kanters 891392ff9a Revert "Fix Home Assistant installed_version error (#15369)" (#15396)
This reverts commit ba2d9aefe6.
2022-12-06 17:31:48 +01:00
Koen Kanters ba2d9aefe6 Fix Home Assistant installed_version error (#15369)
* Fix HA installed_version error

* add link
2022-12-05 16:52:11 +01:00
github-actions[bot]andKoenkk 586dda4d46 Update zigbee-herdsman-converters to 14.0.683 (#15356)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-04 20:09:14 +00:00
github-actions[bot]andKoenkk 1aa600ee59 Update zigbee-herdsman to 0.14.77 (#15355)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-04 19:56:20 +00:00
Pheel Tiandphil b0e352bfea Support HTTPS for frontend server (#15329)
* Adding 2 config params under 'frontend' : 'ssl_cert' and 'ssl_key' to contain file patch of certificate files to expose frontend in HTTPS.

* Taking all PR remarks in account.

Co-authored-by: phil <phil@8godsprod.com>
2022-12-04 19:33:04 +01:00
github-actions[bot]andKoenkk 959ca95219 Update dependencies (#15342)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-04 19:13:38 +01:00
github-actions[bot]andKoenkk 54bd9aa024 Update zigbee-herdsman-converters to 14.0.682 (#15320)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-02 16:55:00 +00:00
github-actions[bot]andKoenkk 0e4ea6a335 Update zigbee-herdsman-converters to 14.0.681 (#15287)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-01 19:18:59 +00:00
Koen Kanters ab1261385b Promote to dev 2022-12-01 19:59:17 +01:00
Koen Kanters 52e545f900 1.28.4 2022-12-01 19:58:42 +01:00
Koen Kanters 350abb354b Undev 2022-12-01 19:58:39 +01:00
Koen Kanters 87f253997c Update zhc to 14.0.681-hotfix.0 2022-12-01 19:56:37 +01:00
Koen Kanters 4e9b18fcbf Promote to dev 2022-12-01 17:38:11 +01:00
Koen Kanters e43c371d76 1.28.3 2022-12-01 17:35:02 +01:00
Koen Kanters a7e85a0bd9 Undev 2022-12-01 17:34:47 +01:00
Koen Kanters 7f6f6a69c7 Merge remote-tracking branch 'origin/master' into dev 2022-12-01 17:33:55 +01:00
github-actions[bot]andKoenkk 87667bdac7 Update zigbee-herdsman-converters to 14.0.680 (#15276)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-01 15:02:29 +00:00
github-actions[bot]andKoenkk bd8edd70bb Update zigbee-herdsman-converters to 14.0.679 (#15262)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-30 16:06:01 +00:00
github-actions[bot]andKoenkk 4a3e6b9561 Update zigbee-herdsman-converters to 14.0.678 (#15249)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-29 18:45:39 +00:00
Artem Draft f92134fc5f Disable by default update_available sensor in HA (#15237) 2022-11-29 19:18:33 +01:00
9dba2d7f11 Update zigbee-herdsman-converters to 14.0.677 (#15229)
* Update zigbee-herdsman-converters to 14.0.677

* Fix

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-11-28 18:49:34 +00:00
9289a26f11 Update dependencies (#15204)
* Update dependencies

* Fix diff

* fix

* update

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-11-27 14:52:47 +01:00
github-actions[bot]andKoenkk 17054627e9 Update zigbee-herdsman-converters to 14.0.676 (#15207)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-27 09:38:39 +00:00
github-actions[bot]andKoenkk 66c4ca072e Update zigbee-herdsman-converters to 14.0.675 (#15190)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-26 09:32:55 +00:00
github-actions[bot]andKoenkk 3b221d4fb0 Update zigbee-herdsman-converters to 14.0.674 (#15168)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-24 16:40:12 +00:00
github-actions[bot]andKoenkk 4464554f90 Update zigbee-herdsman to 0.14.76 (#15167)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-24 16:32:05 +00:00
b15cd433c4 Update zigbee-herdsman-converters to 14.0.673 (#15150)
* Update zigbee-herdsman-converters to 14.0.673

* Update homeassistant.test.js

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-11-23 17:56:32 +00:00
github-actions[bot]andKoenkk 4b5a03c6c5 Update zigbee-herdsman to 0.14.75 (#15149)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-23 16:10:46 +00:00
github-actions[bot]andKoenkk ca4badca4d Update zigbee-herdsman-converters to 14.0.672 (#15133)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-22 19:03:36 +00:00
DJTerentjevandAlex 1b3dd7540b Add polling when bounded for Trust and Gledopto (#15126)
* GitHub Workflows security hardening (#14799)

* build: harden update_frontend.yml permissions

Signed-off-by: Alex <aleksandrosansan@gmail.com>

* build: harden update_zhc.yml permissions

Signed-off-by: Alex <aleksandrosansan@gmail.com>

* build: harden ghcr_cleanup.yml permissions

Signed-off-by: Alex <aleksandrosansan@gmail.com>

* build: harden update_zh.yml permissions

Signed-off-by: Alex <aleksandrosansan@gmail.com>

Signed-off-by: Alex <aleksandrosansan@gmail.com>

* Add bound polling by manufacturerNames

Based on:
#7825

* Add bound polling by manufacturerNames

Based on:
#7825

Signed-off-by: Alex <aleksandrosansan@gmail.com>
Co-authored-by: Alex <aleksandrosansan@gmail.com>
2022-11-22 19:35:15 +01:00
Koen Kanters c7183ca080 Update external converter tester. 2022-11-22 19:16:25 +01:00
Koen Kanters 119531e9e2 Add external converter tester 2022-11-22 19:12:03 +01:00
85be7d28d5 Update zigbee-herdsman-converters to 14.0.671 (#15123)
* Update zigbee-herdsman-converters to 14.0.671

* Update homeassistant.ts

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-11-21 19:35:37 +00:00
github-actions[bot]andnurikk 6c147dae6c Update zigbee2mqtt-frontend to 0.6.119 (#15109)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-11-20 20:48:42 +00:00
github-actions[bot]andKoenkk 1fe138c34c Update zigbee-herdsman-converters to 14.0.670 (#15107)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-20 18:26:44 +00:00
github-actions[bot]andKoenkk 1258aee464 Update zigbee-herdsman to 0.14.74 (#15105)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-20 16:35:01 +00:00
github-actions[bot]andnurikk fbb0ed0e67 Update zigbee2mqtt-frontend to 0.6.118 (#15083)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-11-19 11:16:56 +00:00
github-actions[bot]andKoenkk 8510e99010 Update zigbee-herdsman-converters to 14.0.669 (#15053)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-17 17:07:37 +00:00
Koen Kanters 342955666a Fix 'dict object' has no attribute 'installed_version'. https://github.com/Koenkk/zigbee2mqtt/discussions/15039 2022-11-17 17:52:59 +01:00
github-actions[bot]andKoenkk 50cb421f64 Update zigbee-herdsman-converters to 14.0.668 (#15007)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-15 19:38:26 +00:00
github-actions[bot]andnurikk 16398b7810 Update zigbee2mqtt-frontend to 0.6.117 (#14995)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-11-15 11:36:57 +00:00
github-actions[bot]andKoenkk f3b5aee173 Update zigbee-herdsman-converters to 14.0.667 (#14983)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-14 20:16:38 +00:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> e8f85b35c1 Bump actions/dependency-review-action from 2 to 3 (#14978)
Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 2 to 3.
- [Release notes](https://github.com/actions/dependency-review-action/releases)
- [Commits](https://github.com/actions/dependency-review-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/dependency-review-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-14 18:13:49 +01:00
github-actions[bot]andKoenkk dd8f13aa8d Update zigbee-herdsman to 0.14.73 (#14979)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-14 17:07:35 +00:00
dreed47 ced2ba7344 small clarification to the description for adding a user-defined device icon (#14960)
* small clarification to the description for adding a user-defined device icon

* small wording change
2022-11-14 17:49:18 +01:00
Jakub Jirutka a2f8534823 Allow to specify full file name in value reference '!' (#14955)
* Extract parsing of value reference '!' in settings into a function

* Allow to specify full file name in value reference '!'

This `network_key: '!secret network_key'` is really confusing because
it looks like a YAML tag named 'secret' and a value "network_key".
When we write `network_key: '!secret.yaml network_key'` instead, it's
a little clearer what's going on. Also, it's more consistent with file
references in 'devices' and 'groups'.
2022-11-13 15:17:59 +01:00
github-actions[bot]andKoenkk d05ef70901 Update zigbee-herdsman to 0.14.72 (#14958)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-13 09:26:45 +00:00
Jakub JirutkaandKoen Kanters 4bc0c3ed6b Use path.resolve instead of path.join in data.joinPath (#14953)
This allows specifying a path outside of the data directory using an
absolute path (e.g. /path/to/devices.yaml) or a path relative to the data
directory (e.g. devices.yaml, foo/devices.yaml, ../../devices.yaml, ...).
This change is backward compatible because file names and nested paths are
just a special case of a relative path.

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-11-13 09:24:47 +01:00
Koen Kanters 489d1de756 Fix Cannot set properties of undefined (setting 'installed_version'). https://github.com/Koenkk/zigbee2mqtt/issues/14917 2022-11-13 09:21:27 +01:00
github-actions[bot]andKoenkk 499b993410 Update zigbee-herdsman to 0.14.71 (#14946)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-12 15:20:46 +00:00
github-actions[bot]andKoenkk 9f0b16291a Update zigbee-herdsman-converters to 14.0.666 (#14930)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-11 16:16:51 +00:00
github-actions[bot]andKoenkk 98a6a2dfe8 Update zigbee-herdsman-converters to 14.0.665 (#14916)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-10 17:05:54 +00:00
Koen Kanters fb14ba20ca Update Docker image to use node:18-alpine3.16 (#14915) 2022-11-10 17:48:17 +01:00
Koen Kanters b68b984de6 Log Zigbee2MQTT started! on startup. https://github.com/Koenkk/zigbee2mqtt/issues/14283 2022-11-10 17:45:48 +01:00
Koen Kanters 1d36dfd932 Support Home Assistant MQTT update #14748 (#14905) 2022-11-09 21:01:30 +01:00
github-actions[bot]andKoenkk 672da64913 Update zigbee-herdsman-converters to 14.0.664 (#14900)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-09 18:53:08 +00:00
github-actions[bot]andKoenkk cb29e164f8 Update zigbee-herdsman-converters to 14.0.663 (#14889)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-08 18:20:43 +00:00
Vincenzo Mantova 1e6a457190 more Home Assistant icons (#14865)
* add missing icons and classes for BRT-100-TRV (and possibly others)

* use high/low thermometer icons for max/min temperatures
2022-11-08 16:24:19 +01:00
github-actions[bot]andKoenkk cd22dafc89 Update zigbee-herdsman-converters to 14.0.662 (#14860)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-07 17:34:17 +00:00
github-actions[bot]andKoenkk 83d8a0e6a2 Update zigbee-herdsman to 0.14.70 (#14858)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-07 17:06:27 +00:00
Vincenzo Mantova e0b765ba88 Revert "Respect climate system_mode ACCESS_SET bit in Home Assistant (#14715)" (#14842)
This reverts commit 58c800181a.
2022-11-07 17:38:30 +01:00
5e2df18b75 Update zigbee-herdsman-converters to 14.0.661 (#14839)
* Update zigbee-herdsman-converters to 14.0.661

* Fix tests

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-11-06 14:42:08 +00:00
github-actions[bot]andKoenkk 9b48334e0e Update zigbee-herdsman to 0.14.69 (#14838)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-06 12:51:48 +00:00
github-actions[bot]andKoenkk c8ae15e92d Update dependencies (#14834)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-06 13:37:56 +01:00
John DoeandAinur Timerbaev f88ac154a2 Mark node v19 as supported (#14837)
* Mark node19 as supported

* Fix condition

* Fix tests

Co-authored-by: Ainur Timerbaev <ainur.timerbaev@superawesome.com>
2022-11-06 12:28:59 +00:00
Alex e66fe8534e GitHub Workflows security hardening (#14799)
* build: harden update_frontend.yml permissions

Signed-off-by: Alex <aleksandrosansan@gmail.com>

* build: harden update_zhc.yml permissions

Signed-off-by: Alex <aleksandrosansan@gmail.com>

* build: harden ghcr_cleanup.yml permissions

Signed-off-by: Alex <aleksandrosansan@gmail.com>

* build: harden update_zh.yml permissions

Signed-off-by: Alex <aleksandrosansan@gmail.com>

Signed-off-by: Alex <aleksandrosansan@gmail.com>
2022-11-05 14:21:55 +01:00
Jakub Jirutka c2e95bdb6a Remove core-js polyfills (#14788)
According to engines.node in package.json, the minimal supported version
of Node.js is 14.

* `Object.fromEntries()` is supported by Node.js since 12.0.0
* `Array.prototype.flat()` is supported by Node.js since 11.0.0

These polyfills were added in #9085, but the minimum supported Node.js
version has since been raised to 14.
2022-11-05 14:17:53 +01:00
github-actions[bot]andnurikk 7c5ef8a3d6 Update zigbee2mqtt-frontend to 0.6.115 (#14816)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-11-05 09:59:34 +00:00
Koen Kanters 4a2190f969 Add back fsevents 2022-11-04 19:35:14 +01:00
github-actions[bot]andKoenkk a3ed236524 Update dependencies (#14810)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-04 17:08:56 +01:00
Koen Kanters f4d8e6f4c5 Merge branch 'master' into dev 2022-11-04 17:05:54 +01:00
Koen Kanters 360a777a4e 1.28.2 2022-11-04 16:53:33 +01:00
Koen Kanters 5ed8b36cf2 Bump zigbee-herdsman-converters to 14.0.657-hotfix.0 2022-11-04 16:52:53 +01:00
github-actions[bot]andKoenkk 9540b6c182 Update zigbee-herdsman-converters to 14.0.660 (#14792)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-03 18:43:32 +00:00
Koen Kanters 31f002294e Update README.md 2022-11-02 22:00:25 +01:00
ecfc7e8906 Update zigbee-herdsman-converters to 14.0.659 (#14780)
* Update zigbee-herdsman-converters to 14.0.659

* Fix tests

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-11-02 20:56:54 +00:00
Koen Kanters 60bf855e74 Fix starting via cli.js. https://github.com/Koenkk/zigbee2mqtt/pull/14396 2022-11-02 20:01:06 +01:00
Koen Kanters c3f6e793fa Promote to dev 2022-11-01 19:59:25 +01:00
Koen Kanters 48117fddfa 1.28.1 2022-11-01 19:58:47 +01:00
Koen Kanters 7e70a5984f Undev 2022-11-01 19:58:44 +01:00
github-actions[bot]andKoenkk 4589c9f691 Update zigbee-herdsman-converters to 14.0.657 (#14753)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-01 18:51:21 +00:00
github-actions[bot]andKoenkk d81c15b5af Update zigbee-herdsman to 0.14.68 (#14754)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-01 16:44:28 +00:00
github-actions[bot]andKoenkk 043ae738d2 Update zigbee-herdsman to 0.14.67 (#14752)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-11-01 16:07:35 +00:00
github-actions[bot]andKoenkk 68184d669e Update zigbee-herdsman-converters to 14.0.655 (#14734)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-31 18:20:01 +00:00
github-actions[bot]andKoenkk fb3ee70c7a Update zigbee-herdsman-converters to 14.0.654 (#14727)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-30 19:07:00 +00:00
github-actions[bot]andKoenkk c5697e6c2b Update dependencies (#14718)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-30 19:23:11 +01:00
Vincenzo Mantova 58c800181a Respect climate system_mode ACCESS_SET bit in Home Assistant (#14715) 2022-10-30 19:16:32 +01:00
github-actions[bot]andKoenkk fd023268c5 Update zigbee-herdsman-converters to 14.0.652 (#14708)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-29 15:50:52 +00:00
github-actions[bot]andKoenkk 9dc93c7d27 Update zigbee-herdsman-converters to 14.0.651 (#14700)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-29 08:21:29 +00:00
Koen Kanters c943992e49 Fix 60de5b7676 2022-10-27 21:49:25 +02:00
github-actions[bot]andKoenkk dd9e92f81f Update zigbee-herdsman-converters to 14.0.650 (#14677)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-27 21:46:16 +02:00
Koen Kanters 60de5b7676 Respond with NO_IMAGE_AVAILABLE when device requests OTA. https://github.com/Koenkk/zigbee2mqtt/issues/13396 2022-10-27 20:39:44 +02:00
github-actions[bot]andKoenkk 6c0430b6e2 Update zigbee-herdsman-converters to 14.0.649 (#14660)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-26 16:57:24 +00:00
github-actions[bot]andKoenkk ddc4345f01 Update zigbee-herdsman-converters to 14.0.648 (#14633)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-24 17:49:52 +00:00
Koen Kanters 41053aa024 Set NODE_ENV to PRODUTION in Docker container. https://github.com/Koenkk/zigbee2mqtt/discussions/14625 2022-10-24 19:30:40 +02:00
github-actions[bot]andKoenkk 08ceae235e Update dependencies (#14602)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-23 09:09:56 +02:00
github-actions[bot]andKoenkk 599380dd63 Update zigbee-herdsman-converters to 14.0.647 (#14592)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-22 15:58:08 +00:00
github-actions[bot]andKoenkk a426ee1041 Update zigbee-herdsman-converters to 14.0.646 (#14576)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-21 14:44:53 +00:00
github-actions[bot]andKoenkk 939fdf0496 Update zigbee-herdsman-converters to 14.0.645 (#14553)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-19 15:59:22 +00:00
github-actions[bot]andKoenkk 3cffc2fdd8 Update zigbee-herdsman to 0.14.66 (#14549)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-19 15:26:35 +00:00
github-actions[bot]andKoenkk 22d2170d41 Update zigbee-herdsman-converters to 14.0.644 (#14540)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-18 18:16:04 +00:00
Vladyslav Pavlov 50a9de4e2b Expose step size of local_temperature_calibration Home Assistant sensor (#14502) 2022-10-18 17:59:32 +02:00
github-actions[bot]andKoenkk 2c12cbaf18 Update zigbee-herdsman to 0.14.65 (#14538)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-18 15:50:13 +00:00
github-actions[bot]andKoenkk ef6826f263 Update dependencies (#14503)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-18 17:38:26 +02:00
github-actions[bot]andKoenkk ceeaabafbc Update zigbee-herdsman-converters to 14.0.643 (#14458)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-12 19:47:13 +00:00
github-actions[bot]andKoenkk c49f5b157d Update zigbee-herdsman-converters to 14.0.642 (#14444)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-11 18:52:33 +00:00
github-actions[bot]andKoenkk 0a71e39253 Update zigbee-herdsman-converters to 14.0.641 (#14426)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-10 15:20:46 +00:00
github-actions[bot]andKoenkk 52a0f68c87 Update zigbee-herdsman-converters to 14.0.640 (#14416)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-09 18:19:11 +00:00
github-actions[bot]andKoenkk 457fc9da7c Update dependencies (#14411)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-09 09:01:32 +02:00
github-actions[bot]andKoenkk 5b39884c13 Update zigbee-herdsman-converters to 14.0.639 (#14403)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-08 12:18:16 +00:00
Matt 4d7b1a6c03 Allow Zigbee2MQTT to be installed as a module (#14396)
* Fix mnodule resolution

Allows the correct module path to be found if installed as a package

* Correct signature to avoid TS error

* Allow module to be included in another module

Only create hash ot auto-start if it is the main module, otherwise export the `start` function
2022-10-08 10:48:15 +02:00
Koen Kanters 50b5c67dde Support effects for Home Assistant light groups. #13438 2022-10-08 10:22:26 +02:00
Ben Windsor 005ada0237 Make temperature sensor selection for thermostats appear as a config item in HA (#14376) 2022-10-07 16:58:06 +02:00
github-actions[bot]andKoenkk e9b1080734 Update zigbee-herdsman-converters to 14.0.638 (#14374)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-06 16:12:23 +00:00
Ben Windsor 56cc1432e9 Mark min_temperature_limit as a config variable for HA (#14358) 2022-10-06 17:31:48 +02:00
github-actions[bot]andKoenkk e3dcc7b0a6 Update zigbee-herdsman-converters to 14.0.637 (#14353)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-05 19:08:45 +00:00
github-actions[bot]andKoenkk 37aea8df8f Update zigbee-herdsman to 0.14.64 (#14348)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-05 14:30:54 +00:00
github-actions[bot]andKoenkk f8d64b67ad Update zigbee-herdsman-converters to 14.0.636 (#14331)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-04 17:30:49 +00:00
github-actions[bot]andKoenkk 77fc3a9308 Update dependencies (#14279)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-04 19:26:54 +02:00
Ben Windsor db3730ea44 Expose value_step to home assistant if it has been set on a numeric type (#14313) 2022-10-04 18:02:35 +02:00
github-actions[bot]andnurikk 98ceaa04bb Update zigbee2mqtt-frontend to 0.6.114 (#14290)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-10-02 16:51:48 +00:00
github-actions[bot]andnurikk e3e4a0a440 Update zigbee2mqtt-frontend to 0.6.113 (#14271)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-10-01 09:41:07 +00:00
github-actions[bot]andKoenkk 52150b7257 Update zigbee-herdsman to 0.14.63 (#14270)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-01 08:53:48 +00:00
Koen Kanters 5727fe3a49 Dev 2022-10-01 09:40:35 +02:00
Koen Kanters 03ba647dc6 1.28.0 2022-10-01 09:39:09 +02:00
Koen Kanters be1e9b4421 Undev 2022-10-01 09:38:55 +02:00
github-actions[bot]andKoenkk 57ce1e98a7 Update zigbee-herdsman-converters to 14.0.635 (#14268)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-10-01 07:33:47 +00:00
github-actions[bot]andKoenkk 3d2d4fd298 Update zigbee-herdsman-converters to 14.0.633 (#14262)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-30 18:58:14 +00:00
github-actions[bot]andKoenkk 2258ed1dfa Update zigbee-herdsman to 0.14.62 (#14259)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-30 18:02:25 +00:00
github-actions[bot]andKoenkk 2d750f8b9a Update zigbee-herdsman-converters to 14.0.632 (#14254)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-29 20:31:02 +00:00
b54e29917e Update zigbee-herdsman to 0.14.61 (#14238)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-09-28 18:28:27 +00:00
Markus WegmannandKoen Kanters 3b60a8e02a Fix cover state publishing of multi-endpoint devices (#14200)
* Fix cover state publishing of multi-endpoint devices

* WIP testing

* WIP test for multiEndpoint cover device

* WIP zigfred plus test

* WIP zigfred plus test

* Remove zigfred plus meta testing flag

* Working zigfred plus integration into bridge test

* Working integration of zigfred plus in tests

* WIP zigfred plus testing

* Add working test for multi-cover devices. Working cover state for multi-cover + multi-light devices.

* Fix republishing of cover state

* Fix cover position and tilt readout with postfix topic

* Fix homeassistant.test.js for zigfred plus cover

* Fix cover value template to support multi-cover devices

* Update homeassistant.ts

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-09-28 20:21:30 +02:00
github-actions[bot]andKoenkk a95d47fef9 Update zigbee-herdsman-converters to 14.0.631 (#14232)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-28 15:14:56 +00:00
github-actions[bot]andKoenkk d385d6c1af Update zigbee-herdsman to 0.14.60 (#14231)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-28 15:12:36 +00:00
github-actions[bot]andnurikk 3453585fd0 Update zigbee2mqtt-frontend to 0.6.112 (#14216)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-09-27 15:55:09 +00:00
github-actions[bot]andKoenkk 7b72b85cf7 Update zigbee-herdsman-converters to 14.0.630 (#14186)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-25 08:15:30 +00:00
github-actions[bot]andKoenkk 47378c46bd Update dependencies (#14180)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-25 09:16:35 +02:00
github-actions[bot]andKoenkk 647d45c4fa Update zigbee-herdsman-converters to 14.0.629 (#14171)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-23 20:03:53 +00:00
github-actions[bot]andKoenkk 6775423b86 Update zigbee-herdsman to 0.14.59 (#14167)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-23 18:13:43 +00:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 3413d472ab Bump actions/stale from 5 to 6 (#14160)
Bumps [actions/stale](https://github.com/actions/stale) from 5 to 6.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-23 19:49:49 +02:00
github-actions[bot]andKoenkk d654e085ae Update zigbee-herdsman-converters to 14.0.628 (#14143)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-22 18:24:44 +00:00
github-actions[bot]andKoenkk 802803479e Update zigbee-herdsman to 0.14.58 (#14142)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-22 18:00:13 +00:00
github-actions[bot]andKoenkk 639babd9ab Update zigbee-herdsman-converters to 14.0.627 (#14127)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-21 18:05:46 +00:00
Koen Kanters 49f7a5ca1d Improve Home Assistant history view for sensors without unit. #14103 2022-09-21 17:11:03 +02:00
github-actions[bot]andKoenkk 615c70f3bb Update zigbee-herdsman to 0.14.57 (#14102)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-20 14:59:08 +00:00
github-actions[bot]andKoenkk 9b4a03c0e8 Update zigbee-herdsman-converters to 14.0.626 (#14083)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-19 16:17:25 +00:00
github-actions[bot]andKoenkk 0ccfb5aded Update zigbee-herdsman to 0.14.56 (#14082)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-19 18:01:46 +02:00
NoneangelandKoen Kanters 1966456814 Add HVAC swing mode to Home Assistant discovery (#13784)
* hvac swing mode

* test: add GW003-AS-IN-TE-FC to tested devices

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-09-19 17:56:58 +02:00
github-actions[bot]andKoenkk 925714022f Update dependencies (#14052)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-19 17:39:10 +02:00
github-actions[bot]andKoenkk 1e24734a6c Update zigbee-herdsman-converters to 14.0.625 (#14022)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-16 07:50:35 +00:00
github-actions[bot]andKoenkk c4fc5335d5 Update zigbee-herdsman to 0.14.55 (#14017)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-15 08:44:52 +00:00
github-actions[bot]andKoenkk 0133afaef0 Update zigbee-herdsman-converters to 14.0.624 (#14016)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-15 08:15:30 +00:00
github-actions[bot]andKoenkk 4d0fd3275b Update zigbee-herdsman-converters to 14.0.623 (#14004)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-14 07:41:56 +00:00
5f992cae76 Update zigbee-herdsman-converters to 14.0.622 (#13994)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-09-13 08:04:17 +00:00
Koen Kanters 5a24e62b51 Fix #ba1e3a06b62d9b53f456065f18807410333da3fd 2022-09-13 10:00:22 +02:00
Koen Kanters ba1e3a06b6 Fix cover value_template properties for multi endpoint devices. #13944 2022-09-13 09:16:24 +02:00
github-actions[bot]andKoenkk 809f5d3ed1 Update zigbee-herdsman-converters to 14.0.621 (#13977)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-12 09:25:13 +00:00
Koen Kanters 125ca61812 Log MQTT disconnect reason. #13965 2022-09-12 09:34:20 +02:00
Koen Kanters d38b8ff5f1 Fix cover state in Home Assistant for multi channel cover controllers. #13944 2022-09-12 09:30:07 +02:00
github-actions[bot]andKoenkk 3f7e9941e2 Update zigbee-herdsman-converters to 14.0.619 (#13961)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-11 08:13:59 +00:00
github-actions[bot]andKoenkk 8ba0fe9367 Update dependencies (#13955)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-11 09:39:20 +02:00
github-actions[bot]andKoenkk cbf75d6339 Update zigbee-herdsman-converters to 14.0.618 (#13949)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-10 07:51:44 +00:00
github-actions[bot]andKoenkk 0ea528f750 Update zigbee-herdsman-converters to 14.0.617 (#13936)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-09 07:51:25 +00:00
github-actions[bot]andKoenkk d12b6e0ca9 Update zigbee-herdsman to 0.14.54 (#13935)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-09 07:20:30 +00:00
github-actions[bot]andKoenkk 2b1d1ff6ef Update zigbee-herdsman-converters to 14.0.616 (#13915)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-08 08:11:04 +00:00
github-actions[bot]andKoenkk fc7de95d1d Update zigbee-herdsman-converters to 14.0.614 (#13897)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-07 08:16:05 +00:00
Koen Kanters 2bca5deb66 Fix setting devices/groups to file via environment variables not working. #7807 2022-09-07 10:07:16 +02:00
github-actions[bot]andKoenkk 8be8c4df56 Update zigbee-herdsman-converters to 14.0.613 (#13883)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-06 08:56:30 +00:00
Koen KantersandJohn Doe 9519ffdac7 Add install code support (#13780)
* Add install code support

* Update bridge.ts

Co-authored-by: John Doe <nurikk@users.noreply.github.com>
2022-09-06 09:30:03 +02:00
github-actions[bot]andKoenkk 0b645a4e6a Update zigbee-herdsman-converters to 14.0.611 (#13868)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-05 08:02:00 +00:00
github-actions[bot]andKoenkk 67a94621ab Update dependencies (#13842)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-05 09:10:10 +02:00
github-actions[bot]andnurikk 24d83b05f7 Update zigbee2mqtt-frontend to 0.6.111 (#13841)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-09-03 21:26:02 +00:00
Koen Kanters 178f72c304 Promote to dev 2022-09-01 11:27:14 +02:00
Koen Kanters 7dc48fb6d6 1.27.2 2022-09-01 11:24:20 +02:00
Koen Kanters 5017f556fa Undev 2022-09-01 11:24:17 +02:00
github-actions[bot]andKoenkk 437eeb350c Update zigbee-herdsman to 0.14.53 (#13793)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-01 11:23:29 +02:00
github-actions[bot]andKoenkk cfd792dc70 Update zigbee-herdsman to 0.14.52 (#13792)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-09-01 09:11:55 +00:00
Koen Kanters 2a4722bd38 Promote to dev 2022-09-01 08:42:29 +02:00
Koen Kanters d381f2daa4 1.27.1 2022-09-01 08:41:47 +02:00
Koen Kanters b05b4adf57 Undev 2022-09-01 08:40:53 +02:00
Koen Kanters 96d72103f2 Merge branch 'dev' 2022-09-01 08:37:54 +02:00
github-actions[bot]andKoenkk 97ffa9610b Update zigbee-herdsman-converters to 14.0.610 (#13782)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-31 16:46:59 +00:00
github-actions[bot]andKoenkk 09d6c8601c Update zigbee-herdsman to 0.14.51 (#13781)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-31 16:04:14 +00:00
github-actions[bot]andKoenkk fc409a6884 Update zigbee-herdsman-converters to 14.0.609 (#13769)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-30 15:38:45 +00:00
github-actions[bot]andnurikk fa63ede4b6 Update zigbee2mqtt-frontend to 0.6.110 (#13757)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-08-29 19:21:24 +00:00
github-actions[bot]andKoenkk fd24c8186e Update zigbee-herdsman to 0.14.50 (#13753)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-29 18:06:26 +00:00
github-actions[bot]andKoenkk 0816d07b93 Update zigbee-herdsman-converters to 14.0.608 (#13752)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-29 16:39:36 +00:00
github-actions[bot]andnurikk c1b9345613 Update zigbee2mqtt-frontend to 0.6.109 (#13748)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-08-29 10:30:15 +00:00
github-actions[bot]andKoenkk 7f198d18ac Update zigbee-herdsman-converters to 14.0.607 (#13739)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-28 17:49:58 +00:00
github-actions[bot]andKoenkk 802bccd1f3 Update zigbee-herdsman to 0.14.49 (#13736)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-28 15:26:35 +00:00
github-actions[bot]andKoenkk 77bd76d1ca Update zigbee-herdsman-converters to 14.0.606 (#13710)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-27 08:14:22 +00:00
github-actions[bot]andKoenkk 8dd8b2848c Update zigbee-herdsman-converters to 14.0.605 (#13703)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-26 19:23:43 +00:00
github-actions[bot]andKoenkk c5d54a845a Update zigbee-herdsman-converters to 14.0.604 (#13687)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-25 16:04:17 +00:00
github-actions[bot]andKoenkk f1a19151fc Update zigbee-herdsman-converters to 14.0.603 (#13670)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-24 17:08:37 +00:00
963c2d86d3 Update zigbee-herdsman-converters to 14.0.602 (#13654)
* Update zigbee-herdsman-converters to 14.0.602

* Fix tests

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-08-23 19:53:49 +00:00
9e6b4083b2 Update zigbee-herdsman-converters to 14.0.601 (#13646)
* Update zigbee-herdsman-converters to 14.0.601

* Fix tests

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-08-22 18:06:45 +00:00
github-actions[bot]andKoenkk f29c4a24c2 Update zigbee-herdsman-converters to 14.0.600 (#13634)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-21 18:23:36 +00:00
github-actions[bot]andKoenkk a75e1c2b2b Update dependencies (#13630)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-21 09:30:25 +02:00
github-actions[bot]andKoenkk 62499483ce Update zigbee-herdsman-converters to 14.0.599 (#13618)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-20 07:39:24 +00:00
github-actions[bot]andKoenkk 304bb7b9d1 Update zigbee-herdsman-converters to 14.0.598 (#13611)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-19 19:06:19 +00:00
github-actions[bot]andKoenkk 15a069e3c7 Update zigbee-herdsman to 0.14.48 (#13610)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-19 19:01:11 +00:00
Koen Kanters e0a9323d6e Update dont-stale label 2022-08-19 20:57:44 +02:00
github-actions[bot]andKoenkk a0182c2e39 Update zigbee-herdsman-converters to 14.0.597 (#13596)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-18 17:23:05 +00:00
github-actions[bot]andKoenkk 327f816c0e Update zigbee-herdsman-converters to 14.0.596 (#13586)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-17 18:29:55 +00:00
github-actions[bot]andnurikk 040d2a1b36 Update zigbee2mqtt-frontend to 0.6.108 (#13573)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-08-16 20:51:02 +00:00
github-actions[bot]andKoenkk 497a48b8b0 Update zigbee-herdsman-converters to 14.0.595 (#13569)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-16 18:59:33 +00:00
github-actions[bot]andKoenkk 36a4754430 Update zigbee-herdsman-converters to 14.0.594 (#13542)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-14 16:29:56 +00:00
Koen Kanters 60b9f6f6b2 Home Assistant discovery: add enums with only SET (e.g. selftest). https://github.com/Koenkk/zigbee2mqtt/issues/13532 2022-08-14 18:22:35 +02:00
github-actions[bot]andKoenkk 2bdc7b7aca Update dependencies (#13535)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-14 09:58:14 +02:00
github-actions[bot]andKoenkk fea5d44a57 Update zigbee-herdsman-converters to 14.0.593 (#13517)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-12 14:59:07 +00:00
Koen Kanters 281e3870d3 Fix tests 2022-08-11 20:32:01 +02:00
Koen Kanters c9b2a7f6d4 Revert "Publish device and bridge availability when Home Assistant comes online. https://github.com/Koenkk/zigbee2mqtt/issues/13382"
This reverts commit 619d5232db.
2022-08-11 20:23:40 +02:00
Koen Kanters 6314b86d9b Better fix for #13382 2022-08-11 20:23:19 +02:00
Koen Kanters 619d5232db Publish device and bridge availability when Home Assistant comes online. https://github.com/Koenkk/zigbee2mqtt/issues/13382 2022-08-11 19:50:46 +02:00
Koen Kanters da4a3b9a77 Fix exception when using group remove_all. https://github.com/Koenkk/zigbee2mqtt/issues/13484 2022-08-10 22:38:04 +02:00
github-actions[bot]andKoenkk 62284dbd1a Update zigbee-herdsman-converters to 14.0.592 (#13489)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-10 17:41:21 +00:00
wysiwyng addb09275a Add category and icon for flip_indicator_light Home Assistant discovery (#13477) 2022-08-10 16:57:31 +02:00
github-actions[bot]andKoenkk 9c45a5fdf0 Update zigbee-herdsman-converters to 14.0.591 (#13469)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-09 15:06:24 +00:00
Koen Kanters 0aa29a0fab Home Assistant discovery: always expose cover state. https://github.com/zigbee2mqtt/hassio-zigbee2mqtt/issues/359 2022-08-09 17:01:22 +02:00
github-actions[bot]andKoenkk 5f778a7570 Update zigbee-herdsman-converters to 14.0.590 (#13458)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-08 15:54:36 +00:00
github-actions[bot]andKoenkk e8f6def246 Update zigbee-herdsman-converters to 14.0.589 (#13447)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-07 16:27:47 +00:00
github-actions[bot]andKoenkk 99485fa324 Update dependencies (#13441)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-07 09:20:22 +02:00
github-actions[bot]andKoenkk 483ff4aa8a Update zigbee-herdsman-converters to 14.0.588 (#13424)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-06 08:02:28 +00:00
github-actions[bot]andKoenkk b6c0afc129 Update zigbee-herdsman-converters to 14.0.587 (#13402)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-04 19:55:35 +00:00
github-actions[bot]andKoenkk 7def5ea9bf Update zigbee-herdsman to 0.14.47 (#13394)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-03 16:06:19 +00:00
github-actions[bot]andKoenkk 4e590ece19 Update zigbee-herdsman-converters to 14.0.586 (#13376)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-02 17:42:22 +00:00
c2faa3815a Update zigbee-herdsman-converters to 14.0.585 (#13362)
* Update zigbee-herdsman-converters to 14.0.585

* Fix tests

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-08-01 18:12:28 +00:00
Koen Kanters 52f8753a09 Promote to dev 2022-08-01 17:44:02 +02:00
Koen Kanters a9b8808237 1.27.0 2022-08-01 17:43:26 +02:00
Koen Kanters 26b323e1f6 Undev 2022-08-01 17:43:20 +02:00
Koen Kanters 5990bb384a Merge branch 'dev' 2022-08-01 17:42:35 +02:00
github-actions[bot]andKoenkk be70b83be5 Update zigbee-herdsman-converters to 14.0.583 (#13355)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-08-01 15:08:54 +00:00
github-actions[bot]andKoenkk 7f28c557cd Update dependencies (#13340)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-31 09:55:29 +02:00
github-actions[bot]andKoenkk b1f9f5f3b3 Update zigbee-herdsman-converters to 14.0.582 (#13332)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-30 08:52:22 +00:00
github-actions[bot]andKoenkk f5740021a1 Update zigbee-herdsman-converters to 14.0.581 (#13327)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-29 15:16:24 +00:00
github-actions[bot]andKoenkk 2996cfaf07 Update zigbee-herdsman-converters to 14.0.580 (#13321)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-28 16:33:03 +00:00
github-actions[bot]andKoenkk eda087af99 Update zigbee-herdsman-converters to 14.0.579 (#13303)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-26 18:10:02 +00:00
Koen Kanters 343ef7fd9f Only republish retained messages when none are received. #9629 2022-07-25 19:37:33 +02:00
Koen Kanters 316daee49d Republish retained messages when reconnected to MQTT server. #9629 2022-07-24 22:17:29 +02:00
github-actions[bot]andKoenkk 68aae1a450 Update zigbee-herdsman-converters to 14.0.578 (#13272)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-24 15:23:53 +00:00
github-actions[bot]andKoenkk d92b2f2ea9 Update dependencies (#13266)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-24 16:59:01 +02:00
github-actions[bot]andKoenkk e132a22f20 Update zigbee-herdsman-converters to 14.0.577 (#13260)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-23 15:36:49 +00:00
github-actions[bot]andKoenkk ebcf49b504 Update zigbee-herdsman-converters to 14.0.576 (#13254)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-23 10:30:57 +00:00
github-actions[bot]andKoenkk 6a2de3cd3b Update zigbee-herdsman-converters to 14.0.575 (#13237)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-21 20:58:00 +00:00
github-actions[bot]andKoenkk 8d8da9f286 Update zigbee-herdsman-converters to 14.0.574 (#13226)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-20 18:16:35 +00:00
Andrew Lamchenko 759c2f3f00 Add new clusters for bind (#13214)
* Add new clasters for bind

Add new clasters for bind

* Add new clasters for bind

Add new clasters for bind
2022-07-20 19:41:46 +02:00
github-actions[bot]andKoenkk b140e4bfe6 Update zigbee-herdsman-converters to 14.0.573 (#13213)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-19 18:18:04 +00:00
github-actions[bot]andKoenkk 4ef9ea749d Update zigbee-herdsman-converters to 14.0.571 (#13187)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-17 20:56:28 +00:00
github-actions[bot]andKoenkk 11f3f5b170 Update zigbee-herdsman to 0.14.46 (#13185)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-17 20:30:52 +00:00
github-actions[bot]andKoenkk b6f88aad80 Update dependencies (#13173)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-17 22:14:33 +02:00
Koen Kanters d07b65a03d Fix coverage 2022-07-17 22:14:02 +02:00
Koen Kanters aabc010191 Log error stack on fromZigbee converter error. https://github.com/Koenkk/zigbee2mqtt/issues/13138 2022-07-17 22:03:24 +02:00
github-actions[bot]andnurikk cf8e7b10db Update zigbee2mqtt-frontend to 0.6.107 (#13175)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-07-17 03:15:43 +00:00
github-actions[bot]andKoenkk 63777464cc Update zigbee-herdsman-converters to 14.0.570 (#13162)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-15 15:33:36 +00:00
Koen Kanters 26fc4609f0 Enable publish to ghcr.io 2022-07-15 12:50:43 +02:00
github-actions[bot]andKoenkk 1d8d6fe070 Update zigbee-herdsman-converters to 14.0.569 (#13150)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-14 15:31:24 +00:00
Koen Kanters ca2186a9c5 Finally fix for https://github.com/Koenkk/zigbee2mqtt/issues/13080 2022-07-13 22:04:27 +02:00
github-actions[bot]andKoenkk 7d8b506c49 Update zigbee-herdsman-converters to 14.0.568 (#13134)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-13 17:39:24 +00:00
Koen Kanters 994de20f49 Another attempt to fix https://github.com/Koenkk/zigbee2mqtt/issues/13080 2022-07-13 18:05:45 +02:00
github-actions[bot]andKoenkk f553601ff1 Update zigbee-herdsman to 0.14.45 (#13132)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-13 15:40:54 +00:00
Koen Kanters 3478715926 Update ci.yml 2022-07-12 22:15:26 +02:00
Koen Kanters ed351ee712 TEMP: disable ghcr.io Docker build since ghcr.io is unstable 2022-07-12 22:09:43 +02:00
github-actions[bot]andKoenkk affa15cbae Update zigbee-herdsman to 0.14.44 (#13121)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-12 19:58:07 +00:00
Koen Kanters c6d504a5df Set device_class to temperature for local_temperature_calibration. https://github.com/Koenkk/zigbee2mqtt/issues/13080 2022-07-12 21:45:20 +02:00
Koen Kanters 420b34c909 Fix group and device states changing when cached states are published. https://github.com/Koenkk/zigbee2mqtt/issues/13028 2022-07-12 17:01:29 +02:00
github-actions[bot]andnurikk 4347034ba1 Update zigbee2mqtt-frontend to 0.6.106 (#13110)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-07-12 03:01:48 +00:00
github-actions[bot]andKoenkk ec8e7ed946 Update zigbee-herdsman-converters to 14.0.567 (#13104)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-11 15:57:15 +00:00
Koen Kanters 440fc74368 Fix Home Assistant discovery error: expected NumberDeviceClass or one of 'temperature' for dictionary value @ data['device_class']. https://github.com/Koenkk/zigbee2mqtt/issues/13080 2022-07-11 17:44:08 +02:00
Jorge Schrauwen a0255d0f2c Always treat filtered_* as full matches (#13083)
* Revert "update tests to use regex ^$ anchors"

This reverts commit 3d9bc552e9.

* utils: filterProperties should ensure full match
2022-07-11 17:24:34 +02:00
github-actions[bot]andKoenkk 14216b7454 Update zigbee-herdsman-converters to 14.0.566 (#13093)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-10 08:11:17 +00:00
github-actions[bot]andKoenkk 0da033ce95 Update zigbee-herdsman to 0.14.43 (#13092)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-10 07:57:07 +00:00
3bd3196a40 Update dependencies (#13089)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-07-10 09:23:51 +02:00
github-actions[bot]andnurikk 47e05c32ec Update zigbee2mqtt-frontend to 0.6.105 (#13090)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-07-10 01:55:15 +00:00
github-actions[bot]andKoenkk bcd19509fb Update zigbee-herdsman-converters to 14.0.565 (#13085)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-09 18:32:35 +00:00
github-actions[bot]andKoenkk 05a9920638 Update zigbee-herdsman to 0.14.42 (#13084)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-09 18:05:19 +00:00
Koen Kanters 8af3ad5541 Fix crash when ZIGBEE2MQTT_CONFIG_ADVANCED_NETWORK_KEY environment variable is set to GENERATE. https://github.com/Koenkk/zigbee2mqtt/issues/12999 2022-07-08 16:58:23 +02:00
Koen Kanters 6ef9e4f479 Fix matrix test jobs unstable (#13068)
* Update ci.yml

* Update ci.yml
2022-07-07 15:21:37 +02:00
183dad97e7 Update zigbee-herdsman to 0.14.41 (#13063)
* Update zigbee-herdsman to 0.14.41

* Update package-lock.json

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-07-07 07:44:34 +00:00
github-actions[bot]andKoenkk 3bbdf17fed Update zigbee-herdsman-converters to 14.0.564 (#13062)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-06 15:50:54 +00:00
Jorge Schrauwen db021e6711 Allow regexes for filtered_attributes, filtered_cache and filtered_optimistic options (#13047)
* Ensure filtered_attributes and filtered_optimistic are also regex

Bring filtered_attributes and filtered_optimistic in line with the newer filtered_cache.
All three of them are now regex matches instead of absolute matches.

* update tests to use regex ^$ anchors

* utils: add filterProperties
2022-07-06 17:07:22 +02:00
github-actions[bot]andKoenkk 4a58dbf84c Update zigbee-herdsman-converters to 14.0.563 (#13049)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-05 15:20:46 +00:00
github-actions[bot]andKoenkk ee227d85c7 Update zigbee-herdsman-converters to 14.0.562 (#13035)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-04 15:20:19 +00:00
github-actions[bot]andKoenkk d8c0ef8a9c Update dependencies (#13023)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-04 17:05:01 +02:00
github-actions[bot]andKoenkk 53ae00c46f Update zigbee-herdsman-converters to 14.0.561 (#13022)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-02 18:53:10 +00:00
Jorge SchrauwenandKoen Kanters dedbf336af Add filtered_cache option (#12988)
* want a way to prevent attribute from being cached

* Update filtered_cache description to be more clear

* Fix emitStateChange was returning wrong payload in to parameter

* Directly get filtered_cache from entity and forgo parameter to State.set()

* Add tests to cover filtered_cache

* Update state.ts

* Update controller.test.js

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-07-02 20:33:04 +02:00
Sam bc4ffc00b6 add search before issue create requirement (#13021) 2022-07-02 20:28:30 +02:00
5662e6c29e Update zigbee-herdsman-converters to 14.0.560 (#13016)
* Update zigbee-herdsman-converters to 14.0.560

* Fix tests

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-07-01 21:35:16 +00:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 4cc838accf Bump actions/dependency-review-action from 1 to 2 (#13009)
Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 1 to 2.
- [Release notes](https://github.com/actions/dependency-review-action/releases)
- [Commits](https://github.com/actions/dependency-review-action/compare/v1...v2)

---
updated-dependencies:
- dependency-name: actions/dependency-review-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-01 23:10:32 +02:00
github-actions[bot]andnurikk 3b58ac79dc Update zigbee2mqtt-frontend to 0.6.104 (#13008)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-07-01 15:20:07 +00:00
bab764e2be Binding to msRelativeHumidity (#12877)
* Binding to new clusters

Binding to new clusters. This is necessary for new EFEKTA devices.

* Update bind.ts

* Fix tests

Co-authored-by: nurikk <ainur@yojee.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-07-01 17:02:34 +02:00
Koen Kanters 81892ef70d Promote to dev 2022-07-01 16:42:33 +02:00
Koen Kanters cd23aeaa46 1.26.0 2022-07-01 16:41:38 +02:00
Koen Kanters 5d5b731053 Undev 2022-07-01 16:41:00 +02:00
Koen Kanters c973632efd Merge branch 'dev' 2022-07-01 16:40:18 +02:00
github-actions[bot]andKoenkk e0b63f2baa Update zigbee-herdsman-converters to 14.0.559 (#13004)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-07-01 14:39:37 +00:00
github-actions[bot]andKoenkk 3d406c3c0d Update zigbee-herdsman-converters to 14.0.558 (#12984)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-29 16:06:12 +00:00
github-actions[bot]andKoenkk d4811791fc Update zigbee-herdsman to 0.14.40 (#12983)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-29 16:01:43 +00:00
github-actions[bot]andKoenkk 3b7ef63300 Update zigbee-herdsman-converters to 14.0.557 (#12975)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-28 15:15:39 +00:00
github-actions[bot]andKoenkk 8adb2f1247 Update zigbee-herdsman-converters to 14.0.556 (#12967)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-27 17:18:22 +00:00
Vladimir Vitkov 326c2aaa90 Allow to specify MQTT server in secrets (#12904)
* add server, pan_id, ext_pan_id to secrets

* fix tests

* clean secrets before every test run

* add interpolation of mqtt.server, advanced.pan_id, advanced.ext_pan_id and split mqtt.user, mqtt.pass requirement

* remove pan_id and ext_pan_id from secrets processing
2022-06-27 18:42:05 +02:00
e291687870 Update zigbee-herdsman-converters to 14.0.555 (#12951)
* Update zigbee-herdsman-converters to 14.0.555

* Fix tests

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-06-26 17:01:17 +02:00
Koen Kanters 80f8fb4d98 Node 17 is EOL 2022-06-26 16:56:01 +02:00
RoboMagus 402081e39d Fix EnvVar parsing of settings scheme with 'oneOf'. (#12948) 2022-06-26 16:16:44 +02:00
Koen Kanters 05309db92b Fix crash when poll of bounded device fails. https://github.com/Koenkk/zigbee2mqtt/issues/12918 2022-06-26 16:14:21 +02:00
github-actions[bot]andKoenkk d3fa977ebf Update dependencies (#12942)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-26 09:46:15 +02:00
github-actions[bot]andKoenkk 92f7bc8eb5 Update zigbee-herdsman to 0.14.39 (#12926)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-24 14:19:32 +00:00
github-actions[bot]andKoenkk 80b6f1dfa3 Update zigbee-herdsman-converters to 14.0.554 (#12906)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-22 15:20:07 +00:00
github-actions[bot]andKoenkk 1eaa73bb52 Update zigbee-herdsman-converters to 14.0.553 (#12895)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-21 14:39:48 +00:00
github-actions[bot]andKoenkk 0fc831cd82 Update zigbee-herdsman-converters to 14.0.552 (#12886)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-20 16:20:23 +00:00
github-actions[bot]andKoenkk 59c18af1f7 Update dependencies (#12874)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-20 17:47:45 +02:00
github-actions[bot]andnurikk 0fd47c0ad1 Update zigbee2mqtt-frontend to 0.6.103 (#12885)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-06-20 15:32:50 +00:00
github-actions[bot]andnurikk 6b643f0e67 Update zigbee2mqtt-frontend to 0.6.102 (#12884)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-06-20 14:55:48 +00:00
github-actions[bot]andKoenkk 3c9fab143c Update zigbee-herdsman-converters to 14.0.551 (#12871)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-18 17:50:50 +00:00
Koen Kanters f2e953cf18 Add group off_state option (#12868) 2022-06-18 15:47:06 +02:00
github-actions[bot]andKoenkk ecb18a6c41 Update zigbee-herdsman-converters to 14.0.550 (#12864)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-18 09:24:55 +00:00
github-actions[bot]andKoenkk d405c43202 Update zigbee-herdsman-converters to 14.0.549 (#12862)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-18 08:30:31 +00:00
github-actions[bot]andKoenkk 24e7a368cb Update zigbee-herdsman to 0.14.38 (#12861)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-18 08:22:20 +00:00
github-actions[bot]andKoenkk 8848e1e6ce Update zigbee-herdsman-converters to 14.0.548 (#12856)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-17 15:08:56 +00:00
John Doeandnurikk 820c20af80 Fix windows tests (#12840)
Co-authored-by: nurikk <ainur@yojee.com>
2022-06-16 14:00:09 +08:00
github-actions[bot]andnurikk 4b8b951c28 Update zigbee2mqtt-frontend to 0.6.101 (#12839)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-06-16 05:37:34 +00:00
github-actions[bot]andnurikk 8964421350 Update zigbee2mqtt-frontend to 0.6.100 (#12838)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-06-16 05:13:27 +00:00
github-actions[bot]andKoenkk 0895f29a83 Update zigbee-herdsman-converters to 14.0.547 (#12833)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-15 16:00:32 +00:00
Koen Kanters f213729dcb Remove Home Assistant away_mode_* discovery. https://github.com/Koenkk/zigbee2mqtt/issues/11738 2022-06-15 17:56:13 +02:00
rikroeandKoen Kanters 6df20010dd Change HA hold_mode to preset_mode (#12565)
* Update homeassistant.ts

* Update homeassistant.test.js

* Update homeassistant.ts

* Update homeassistant.ts

* Updates

* Updates

* Updates

* Updates

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-06-15 17:40:44 +02:00
github-actions[bot]andKoenkk a5204cdeb1 Update zigbee-herdsman to 0.14.37 (#12832)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-15 15:14:36 +00:00
Koen Kanters dd129830d3 Create herdsman backup before backup. https://github.com/Koenkk/zigbee2mqtt/pull/12797 2022-06-15 17:05:20 +02:00
github-actions[bot]andKoenkk a8233e0521 Update zigbee-herdsman to 0.14.36 (#12831)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-15 17:04:20 +02:00
Koen Kanters 9b2a92106e Disable ghcr_cleanup 2022-06-15 17:03:16 +02:00
github-actions[bot]andKoenkk 8fe6ab4866 Update zigbee-herdsman-converters to 14.0.546 (#12826)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-14 18:36:31 +00:00
Koen Kanters a08ff9bf6d Prepare for zhc 14.0.546 2022-06-14 20:31:25 +02:00
Koen Kanters 83b490a968 Add zigbee2mqtt/bridge/request/backup (#12797)
* Add backup call

* Updates
2022-06-14 20:14:01 +02:00
github-actions[bot]andnurikk 3498c6e260 Update zigbee2mqtt-frontend to 0.6.99 (#12812)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-06-13 14:28:42 +00:00
github-actions[bot]andKoenkk 5c93aaf6db Update zigbee-herdsman-converters to 14.0.545 (#12804)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-12 19:51:52 +00:00
github-actions[bot]andKoenkk e860978564 Update zigbee-herdsman-converters to 14.0.544 (#12796)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-12 07:17:00 +00:00
github-actions[bot]andKoenkk 2d7a6e9f42 Update dependencies (#12795)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-12 08:48:41 +02:00
github-actions[bot]andKoenkk 1390bc1d04 Update zigbee-herdsman-converters to 14.0.543 (#12783)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-11 08:12:40 +00:00
github-actions[bot]andKoenkk a9349ac49d Update zigbee-herdsman to 0.14.35 (#12782)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-11 08:07:05 +00:00
github-actions[bot]andKoenkk 5bed214aef Update zigbee-herdsman-converters to 14.0.542 (#12781)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-11 07:53:34 +00:00
github-actions[bot]andKoenkk 4f181b0107 Update zigbee-herdsman-converters to 14.0.541 (#12767)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-09 19:25:49 +00:00
Koen Kanters 8e2f0f613f Send cached group state on startup and when Home Assistant comes online. https://github.com/Koenkk/zigbee2mqtt/issues/12693 2022-06-09 20:52:39 +02:00
github-actions[bot]andKoenkk 2bb4f91d1c Update zigbee-herdsman-converters to 14.0.540 (#12756)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-08 18:29:55 +00:00
Koen Kanters 389ff844c4 Revert "Revert "Revert "Temp: disable ghcr.io since it makes build fail"""
This reverts commit 975eb7d6ae.
2022-06-08 16:40:20 +02:00
github-actions[bot]andKoenkk cb0542eaad Update zigbee-herdsman-converters to 14.0.539 (#12746)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-07 16:17:18 +00:00
Koen Kanters 300b2973e1 Update ci.yml 2022-06-06 13:32:44 +02:00
Koen Kanters 5c44582b69 Update zigbee2mqtt/hassio-zigbee2mqtt trigger 2022-06-06 12:58:37 +02:00
github-actions[bot]andKoenkk 2f2a66db2e Update zigbee-herdsman-converters to 14.0.538 (#12728)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-05 20:07:53 +00:00
github-actions[bot]andKoenkk b075d6bbda Update dependencies (#12727)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-05 22:02:11 +02:00
Koen Kanters a252914ecd Update update_deps.yml 2022-06-05 21:48:26 +02:00
github-actions[bot]andKoenkk 87edd1e6f1 Update zigbee-herdsman-converters to 14.0.537 (#12712)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-04 12:25:18 +00:00
github-actions[bot]andKoenkk ba69e1788f Update zigbee-herdsman-converters to 14.0.536 (#12709)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-04 08:42:26 +00:00
Naveen 05538ec28b chore: Enable codeql action (#12708)
This action runs GitHub's industry-leading semantic code analysis engine,
CodeQL, against a repository's source code to find security vulnerabilities.

https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/about-code-scanning-with-codeql

https://github.com/ossf/scorecard/blob/main/docs/checks.md#sast
Signed-off-by: naveen <172697+naveensrinivasan@users.noreply.github.com>
2022-06-04 10:28:44 +02:00
NaveenandKoen Kanters 2676c3f641 chore(deps): Included dependency review (#12695)
* chore(deps): Included dependency review

> Dependency Review GitHub Action in your repository to enforce dependency
> reviews on your pull requests.
> The action scans for vulnerable versions of dependencies introduced by package version
> changes in pull requests,
> and warns you about the associated security vulnerabilities.
> This gives you better visibility of what's changing in a pull request,
> and helps prevent vulnerabilities being added to your repository.

https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement
Signed-off-by: naveen <172697+naveensrinivasan@users.noreply.github.com>

* Update depsreview.yaml

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-06-03 16:41:06 +02:00
github-actions[bot]andKoenkk e525919160 Update zigbee-herdsman-converters to 14.0.535 (#12692)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-02 16:13:26 +00:00
John Doeandnurikk d064a73131 Rework ci and use package-lock.json instead of npm-shrinkwrap.json (#12631)
* Update ci

* Use package-lock

* Use package-lock.json

* Use cache for ci

* Enable node18 windows

* Revert "Enable node18 windows"

This reverts commit e427551acdd64c3a256b48a5b8e86b3c4c986405.

* Remove redundant cache

* Don't do shrinkwrap

* Remove shrinkwrap

* Remove redundant step

* Cache node-gyp

* Debug

* Add key

* Update ci.yml

* Add cache detection

* Fix

* Add deps

* Fix2

* Update caches

* Fix ci

* Update patsh

* Update paths

* Update paths

* Fix max cache path

* Change cache key

* Remove spaces

* Split caches

* Fix linux cache

* Fix linux cache

* Merge caches

* Ignore errs

* Find only dirs

* Run find after ci

* Fix mac cache

* Fix caches

* Remove env run

Co-authored-by: nurikk <ainur@yojee.com>
2022-06-02 14:38:07 +00:00
Koen Kanters 64ab3b0492 Revert "Downgrade docker/setup-qemu-action to v1"
This reverts commit 35b9e01230.
2022-06-02 16:27:18 +02:00
Koen Kanters e1c9d68c68 Revert "Check if ghcr.io build works again"
This reverts commit 1596714ccb.
2022-06-02 16:27:11 +02:00
Koen Kanters 35b9e01230 Downgrade docker/setup-qemu-action to v1 2022-06-02 16:13:40 +02:00
Koen Kanters 1596714ccb Check if ghcr.io build works again 2022-06-02 16:02:51 +02:00
Koen Kanters e852851273 Update ghcr_cleanup.yml 2022-06-02 08:30:55 +02:00
github-actions[bot]andKoenkk 24e9c5bc9f Update zigbee-herdsman-converters to 14.0.534 (#12676)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-01 17:22:40 +00:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> dc4f4ad047 Bump actions/github-script from 3 to 6 (#12674)
Bumps [actions/github-script](https://github.com/actions/github-script) from 3 to 6.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v3...v6)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-01 19:16:21 +02:00
neilnaveenandKoen Kanters cc42ab7584 chore: Set permissions for GitHub actions (#12626)
Restrict the GitHub token permissions only to the required ones; this way, even if the attackers will succeed in compromising your workflow, they won’t be able to do much.

- Included permissions for the action. https://github.com/ossf/scorecard/blob/main/docs/checks.md#token-permissions

https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions

https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs

[Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/)

Signed-off-by: neilnaveen <42328488+neilnaveen@users.noreply.github.com>

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-06-01 19:15:06 +02:00
Koen Kanters 03fdb0cb88 Fix Home Assistant rename creating duplicate entities. #12610 2022-06-01 18:57:02 +02:00
Koen Kanters f7c6518506 Promote to dev 2022-06-01 17:38:46 +02:00
Koen Kanters 3c5854fa9f 1.25.2 2022-06-01 17:38:09 +02:00
Koen Kanters 900175af47 Undev 2022-06-01 17:37:39 +02:00
Koen Kanters 9b9e2289d3 Merge remote-tracking branch 'origin/master' into dev 2022-06-01 17:36:50 +02:00
github-actions[bot]andKoenkk ea10936641 Update zigbee-herdsman-converters to 14.0.533 (#12672)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-01 15:14:02 +00:00
github-actions[bot]andKoenkk 6b56b0bcf8 Update zigbee-herdsman-converters to 14.0.532 (#12671)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-06-01 15:03:10 +00:00
Koen Kanters 975eb7d6ae Revert "Revert "Temp: disable ghcr.io since it makes build fail""
This reverts commit 4e28df88ae.
2022-06-01 16:57:47 +02:00
Koen Kanters 4de04c19c0 Update ghcr_cleanup.yml 2022-06-01 15:12:32 +02:00
Koen Kanters 1db12612e6 Add GHCR cleanup (#12669)
* Create ghcr_cleanup.yml

* Update ghcr_cleanup.yml

* Update ghcr_cleanup.yml

* Update ghcr_cleanup.yml

* Update ghcr_cleanup.yml
2022-06-01 14:18:41 +02:00
github-actions[bot]andKoenkk ec010df79a Update zigbee-herdsman-converters to 14.0.531 (#12659)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-31 15:17:42 +00:00
github-actions[bot]andKoenkk 88b65625de Update zigbee-herdsman to 0.14.34 (#12658)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-31 14:58:08 +00:00
Koen Kanters 4e28df88ae Revert "Temp: disable ghcr.io since it makes build fail"
This reverts commit a1db076c02.
2022-05-31 08:28:36 +02:00
github-actions[bot]andKoenkk 44815435e7 Update zigbee-herdsman-converters to 14.0.530 (#12645)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-30 16:11:37 +00:00
Koen Kanters fe011ba7b3 Disable node 18 testing for now (fails quite often) 2022-05-29 10:21:23 +02:00
Koen Kanters 68035a1256 Fix restart not working (#12629) 2022-05-29 08:15:47 +00:00
github-actions[bot]andKoenkk cc535f11c6 Update zigbee-herdsman-converters to 14.0.529 (#12628)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-29 07:23:01 +00:00
github-actions[bot]andKoenkk 7001b2fc79 Update dependencies (#12627)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-29 06:46:27 +00:00
Koen Kanters 05975109a4 Lock connect-gzip-static https://github.com/Koenkk/zigbee2mqtt/pull/12624/files#r884205347 2022-05-29 08:43:02 +02:00
github-actions[bot]andKoenkk 502443aae8 Update zigbee-herdsman-converters to 14.0.528 (#12615)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-27 19:15:31 +00:00
Koen Kanters 43a82ad835 Delete ghcr_delete_untagged.yml 2022-05-27 16:29:50 +02:00
Koen Kanters a1db076c02 Temp: disable ghcr.io since it makes build fail 2022-05-27 16:23:34 +02:00
Koen Kanters bfc70c3bb8 Update ghcr_delete_untagged.yml 2022-05-27 15:09:48 +02:00
Koen Kanters b268b1ed75 Update ghcr_delete_untagged.yml 2022-05-27 12:08:55 +02:00
Koen Kanters 64769b3e9f Update ghcr_delete_untagged.yml 2022-05-27 12:05:51 +02:00
Koen Kanters 66e804d71e Create ghcr_delete_untagged.yml 2022-05-27 12:01:21 +02:00
github-actions[bot]andKoenkk 1c30a4f4bc Update zigbee-herdsman to 0.14.33 (#12612)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-27 09:51:49 +00:00
Koen Kanters be55e3ee80 Revert "Temp disable ghcr.io push (seems to make build fail)"
This reverts commit c1372852e9.
2022-05-27 11:47:43 +02:00
github-actions[bot]andnurikk 1aa7971ca1 Update zigbee2mqtt-frontend to 0.6.97 (#12605)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-05-26 11:25:29 +00:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 6e2ab644a0 Bump peter-evans/create-pull-request from 3 to 4 (#12602)
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 3 to 4.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](https://github.com/peter-evans/create-pull-request/compare/v3...v4)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-26 09:54:13 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 4fa6c33622 Bump peter-evans/enable-pull-request-automerge from 1 to 2 (#12600)
Bumps [peter-evans/enable-pull-request-automerge](https://github.com/peter-evans/enable-pull-request-automerge) from 1 to 2.
- [Release notes](https://github.com/peter-evans/enable-pull-request-automerge/releases)
- [Commits](https://github.com/peter-evans/enable-pull-request-automerge/compare/v1...v2)

---
updated-dependencies:
- dependency-name: peter-evans/enable-pull-request-automerge
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-26 09:53:39 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 3fd405e570 Bump actions/stale from 3 to 5 (#12601)
Bumps [actions/stale](https://github.com/actions/stale) from 3 to 5.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v3...v5)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-26 09:53:15 +02:00
Koen Kanters c1372852e9 Temp disable ghcr.io push (seems to make build fail) 2022-05-26 09:50:39 +02:00
Koen Kanters 95eedc9ec1 Add dependabot.yml 2022-05-26 09:48:43 +02:00
Koen Kanters d7b926d47f Update ci.yml dependencies 2022-05-26 09:43:11 +02:00
github-actions[bot]andKoenkk 67039a1a94 Update zigbee-herdsman-converters to 14.0.527 (#12599)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-26 07:27:42 +00:00
github-actions[bot]andKoenkk f79fb16e41 Update zigbee-herdsman-converters to 14.0.526 (#12595)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-25 16:58:18 +00:00
Koen Kanters d910c05e6e Improve tests 2022-05-25 11:38:37 +02:00
github-actions[bot]andnurikk 4858e06060 Update zigbee2mqtt-frontend to 0.6.96 (#12592)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-05-25 09:31:15 +00:00
f71db6816a Update zigbee-herdsman-converters to 14.0.525 (#12585)
* Update zigbee-herdsman-converters to 14.0.525

* Fix tests

* Ignore

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-05-24 18:27:17 +00:00
github-actions[bot]andnurikk c6b129c68d Update zigbee2mqtt-frontend to 0.6.95 (#12572)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-05-23 04:59:27 +00:00
e5dbe01329 Update zigbee-herdsman-converters to 14.0.522 (#12568)
* Update zigbee-herdsman-converters to 14.0.522

* Fix

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-05-22 20:32:15 +00:00
Koen Kanters c00768b7c7 Move ci before tests 2022-05-21 15:08:07 +02:00
Koen Kanters ad070c99ef Add official support for node 18 and drop for node 10, 12, 15 (#12553) 2022-05-21 08:29:22 +00:00
aec082c0cf Update zigbee-herdsman-converters to 14.0.521 (#12552)
* Update zigbee-herdsman-converters to 14.0.521

* Fixes

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-05-21 08:20:37 +00:00
github-actions[bot]andKoenkk cc84452f23 Update zigbee-herdsman to 0.14.32 (#12551)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-21 07:45:02 +00:00
John Doeandnurikk 84bac2b8d8 Add compatibility tests (#12527)
* Add tests gh action

* Fix zigbee_ota_override_index_location

* Fix Shouldnt crash when it cannot save state test

* Add node17

* Update tests.yml

* Drop old versions

* Move to ci.yml

* Update controller.test.js

* Fix linebraks on windows

* Ignore windows and node 17

* Fail on error

* Add comment

* Mark node 18 as supported

Co-authored-by: nurikk <ainur@yojee.com>
2022-05-21 08:37:39 +02:00
github-actions[bot]andKoenkk 29a9d227de Update zigbee-herdsman-converters to 14.0.520 (#12546)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-20 14:58:14 +00:00
github-actions[bot]andnurikk 39ae0c775a Update zigbee2mqtt-frontend to 0.6.94 (#12540)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-05-20 02:44:04 +00:00
github-actions[bot]andKoenkk f1eaf2d7aa Update zigbee-herdsman-converters to 14.0.519 (#12530)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-18 15:35:44 +00:00
Koen Kanters c6840a9a43 Mark node 17 as supported 2022-05-18 14:56:17 +02:00
John Doeandnurikk 3a1c64a88e Downgrade connect-gzip-static to 2.1.1 (#12525)
Co-authored-by: nurikk <ainur@yojee.com>
2022-05-18 17:30:04 +08:00
github-actions[bot]andnurikk d4a0c93c91 Update zigbee2mqtt-frontend to 0.6.86 (#12524)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-05-18 08:14:56 +00:00
github-actions[bot]andKoenkk 5ae8347317 Update zigbee-herdsman-converters to 14.0.518 (#12518)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-17 15:06:53 +00:00
github-actions[bot]andKoenkk afbfa4631f Update zigbee-herdsman to 0.14.31 (#12504)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-16 16:05:24 +00:00
github-actions[bot]andKoenkk 096f906ac7 Update zigbee-herdsman-converters to 14.0.517 (#12503)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-16 14:44:14 +00:00
github-actions[bot]andnurikk b844f607ee Update zigbee2mqtt-frontend to 0.6.85 (#12502)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-05-16 13:59:21 +00:00
github-actions[bot]andKoenkk 6002bc475b Update dependencies (#12479)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-16 15:19:07 +02:00
github-actions[bot]andKoenkk 24cfdf4594 Update zigbee-herdsman to 0.14.30 (#12466)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-13 13:15:51 +00:00
github-actions[bot]andKoenkk bc5fd1a52f Update zigbee-herdsman to 0.14.29 (#12464)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-13 08:30:16 +00:00
Koen Kanters fd344a3017 Revert "Additional logging for https://github.com/Koenkk/zigbee2mqtt/issues/12432"
This reverts commit 920b4924b0.
2022-05-13 10:05:49 +02:00
github-actions[bot]andKoenkk f9bd184543 Update zigbee-herdsman-converters to 14.0.516 (#12460)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-12 17:53:53 +00:00
github-actions[bot]andKoenkk f2c1e85b4b Update zigbee-herdsman-converters to 14.0.515 (#12447)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-11 18:21:27 +00:00
Koen Kanters 920b4924b0 Additional logging for https://github.com/Koenkk/zigbee2mqtt/issues/12432 2022-05-11 17:22:48 +02:00
github-actions[bot]andKoenkk 99e67b1495 Update zigbee-herdsman-converters to 14.0.514 (#12435)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-10 18:03:45 +00:00
github-actions[bot]andKoenkk e65344c9fa Update zigbee-herdsman-converters to 14.0.513 (#12429)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-09 18:15:01 +00:00
147fe22f08 Update dependencies (#12416)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-05-08 10:01:25 +02:00
Koen Kanters 7f1f1497c6 Always return a device state 2022-05-08 09:58:30 +02:00
Koen Kanters 6278c86d02 Pass device options in global on event. https://github.com/Koenkk/zigbee2mqtt/commit/723dd8358f3cee0b90d624f27989a8bb587e25b0#comments 2022-05-07 14:40:25 +02:00
6f09d45a7b Update zigbee-herdsman-converters to 14.0.512 (#12413)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-05-07 12:39:32 +00:00
Koen Kanters 49a730f77d Update tests for https://github.com/Koenkk/zigbee2mqtt/pull/12413 2022-05-07 14:37:29 +02:00
github-actions[bot]andKoenkk e5a74894d2 Update zigbee-herdsman-converters to 14.0.511 (#12409)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-07 08:43:02 +00:00
Jorge SchrauwenandKoen Kanters 723dd8358f Pass device state to zhc onEvent (#12407)
* onEvent: want device state as argument

* onEvent: test should check deviceOptions and deviceState returns

* Update onEvent.ts

* Update onEvent.ts

* Fix

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-05-07 08:36:03 +00:00
github-actions[bot]andKoenkk 47a22cce2a Update zigbee-herdsman-converters to 14.0.510 (#12403)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-06 15:24:31 +00:00
github-actions[bot]andKoenkk b3791684fd Update zigbee-herdsman-converters to 14.0.509 (#12392)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-05 16:43:18 +00:00
github-actions[bot]andKoenkk ebd3255fc6 Update zigbee-herdsman to 0.14.28 (#12390)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-05 16:17:04 +00:00
github-actions[bot]andKoenkk 9b594a4cba Update zigbee-herdsman-converters to 14.0.508 (#12380)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-04 19:08:08 +00:00
Koen Kanters d0530b8214 Don't allow friendly names to include control character. #12379 2022-05-04 21:00:29 +02:00
5c7e968028 Update zigbee-herdsman-converters to 14.0.507 (#12368)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-05-03 17:42:17 +00:00
Koen Kanters 40ef638e0e Fix null not allowed for Home Assistant device options. #12340 2022-05-03 19:39:30 +02:00
github-actions[bot]andKoenkk b9499618d3 Update zigbee-herdsman-converters to 14.0.506 (#12354)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-02 17:35:37 +00:00
Alex Kay 6618872839 Add new config and diagnostic entities to HA (#12353) 2022-05-02 19:23:40 +02:00
Alex Kay b977dd1140 Add battery2 for HA (#12332) 2022-05-02 14:20:45 +00:00
github-actions[bot]andnurikk 71d5c7b4a2 Update zigbee2mqtt-frontend to 0.6.84 (#12351)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-05-02 12:31:28 +00:00
github-actions[bot]andKoenkk dd8a83e592 Update zigbee-herdsman-converters to 14.0.505 (#12338)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-01 18:45:05 +00:00
Koen Kanters 664182def2 Promote to dev 2022-05-01 20:16:18 +02:00
Koen Kanters 3f6a13786e 1.25.1 2022-05-01 20:14:19 +02:00
Koen Kanters a800197262 Undev 2022-05-01 20:14:12 +02:00
github-actions[bot]andKoenkk 0c145d81cf Update zigbee-herdsman-converters to 14.0.504 (#12336)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-05-01 17:53:38 +00:00
github-actions[bot]andKoenkk 7d7f8aabfe Update zigbee-herdsman-converters to 14.0.503 (#12321)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-30 07:50:44 +00:00
github-actions[bot]andKoenkk 95c2fcfb00 Update zigbee-herdsman-converters to 14.0.502 (#12317)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-29 20:45:59 +00:00
github-actions[bot]andKoenkk 2a6a6c43ce Update zigbee-herdsman-converters to 14.0.501 (#12309)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-28 16:26:57 +00:00
github-actions[bot]andKoenkk 47ed86d735 Update zigbee-herdsman-converters to 14.0.500 (#12294)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-27 07:34:32 +00:00
github-actions[bot]andKoenkk 50990d1f7a Update zigbee-herdsman-converters to 14.0.499 (#12288)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-26 18:05:33 +00:00
Koen Kanters f7b484f24f Log stack trace of "Failed to execute LQI" error. https://github.com/Koenkk/zigbee2mqtt/issues/11968 2022-04-26 20:02:18 +02:00
github-actions[bot]andKoenkk f6ad3c9926 Update zigbee-herdsman-converters to 14.0.498 (#12280)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-25 18:31:28 +00:00
11b415634c Update zigbee-herdsman to 0.14.27 (#12267)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-04-24 18:40:24 +00:00
github-actions[bot]andKoenkk d980d38747 Update zigbee-herdsman-converters to 14.0.497 (#12266)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-24 18:32:09 +00:00
github-actions[bot]andKoenkk 808671dd59 Update dependencies (#12256)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-24 13:55:36 +02:00
Koen Kanters cf22dd60a0 Always respond to OTA request (#12247)
* Always respond to OTA request

* Improve

* Rename
2022-04-23 10:12:29 +02:00
github-actions[bot]andKoenkk 3b820bae9f Update zigbee-herdsman-converters to 14.0.496 (#12249)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-22 18:35:06 +00:00
github-actions[bot]andKoenkk 60a987c004 Update zigbee-herdsman-converters to 14.0.495 (#12240)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-21 15:46:27 +00:00
github-actions[bot]andKoenkk d90be2dc66 Update zigbee-herdsman-converters to 14.0.494 (#12233)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-20 17:09:34 +00:00
Koen Kanters e305ecf571 Improve #ec2bf1a95d6c45c093f68df832bc6bcbdd372a5a 2022-04-20 17:59:16 +02:00
Koen Kanters ec2bf1a95d Fix out of memory error when starting on system with low RAM. #12034 2022-04-20 16:59:30 +02:00
github-actions[bot]andKoenkk 7d5c44a5bd Update zigbee-herdsman-converters to 14.0.493 (#12213)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-19 07:09:28 +00:00
github-actions[bot]andKoenkk 8c4312db72 Update zigbee-herdsman-converters to 14.0.492 (#12210)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-18 17:32:40 +00:00
github-actions[bot]andKoenkk 037a72e0f3 Update zigbee-herdsman-converters to 14.0.491 (#12200)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-17 18:18:25 +00:00
github-actions[bot]andKoenkk d657ea48cf Update dependencies (#12195)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-17 11:47:00 +02:00
github-actions[bot]andKoenkk b4e0c3afcf Update zigbee-herdsman-converters to 14.0.490 (#12193)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-16 07:32:45 +00:00
John Doeandnurikk 27bc646cf6 Add home assistant name to settings schema (#12191)
Co-authored-by: nurikk <ainur@yojee.com>
2022-04-16 10:27:41 +08:00
github-actions[bot]andKoenkk 23ef22e9ba Update zigbee-herdsman-converters to 14.0.489 (#12190)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-15 22:09:50 +00:00
github-actions[bot]andKoenkk 84c8da2532 Update zigbee-herdsman-converters to 14.0.488 (#12186)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-15 15:05:35 +00:00
github-actions[bot]andnurikk e6ec66c844 Update zigbee2mqtt-frontend to 0.6.83 (#12185)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-04-15 14:16:14 +00:00
github-actions[bot]andnurikk d1c26f9aa0 Update zigbee2mqtt-frontend to 0.6.82 (#12181)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-04-15 12:44:03 +00:00
github-actions[bot]andnurikk a7704b9038 Update zigbee2mqtt-frontend to 0.6.81 (#12179)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-04-15 09:36:36 +00:00
github-actions[bot]andKoenkk eabd662757 Update zigbee-herdsman-converters to 14.0.487 (#12172)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-14 15:38:56 +00:00
github-actions[bot]andKoenkk 764895b9d2 Update zigbee-herdsman-converters to 14.0.486 (#12164)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-13 17:48:12 +00:00
Míguel Ángel Mulero MartínezandKoen Kanters 8f5b26a2fb Mark calibration, calibration_time and motor_reversal as config sensors (#12146)
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-04-13 19:37:41 +02:00
github-actions[bot]andKoenkk 533cd36ffd Update zigbee-herdsman to 0.14.26 (#12159)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-12 19:26:50 +02:00
github-actions[bot]andKoenkk 3a77ebf52d Update zigbee-herdsman-converters to 14.0.485 (#12156)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-12 15:11:50 +00:00
github-actions[bot]andKoenkk 0e3bef7e42 Update zigbee-herdsman to 0.14.25 (#12155)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-12 15:00:48 +00:00
github-actions[bot]andKoenkk dda63bd65a Update zigbee-herdsman-converters to 14.0.484 (#12145)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-11 15:59:24 +00:00
github-actions[bot]andKoenkk 2aa1b4ead3 Update zigbee-herdsman-converters to 14.0.483 (#12133)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-10 08:12:57 +00:00
github-actions[bot]andKoenkk 422b7e0168 Update dependencies (#12128)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-10 09:48:40 +02:00
github-actions[bot]andKoenkk 8053eceb84 Update zigbee-herdsman-converters to 14.0.482 (#12124)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-09 16:14:15 +00:00
github-actions[bot]andKoenkk d10d3c9b56 Update zigbee-herdsman-converters to 14.0.481 (#12118)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-08 14:54:54 +00:00
github-actions[bot]andKoenkk 6ff45b1006 Update zigbee-herdsman-converters to 14.0.480 (#12109)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-07 20:39:48 +00:00
github-actions[bot]andKoenkk c901e66e7a Update zigbee-herdsman-converters to 14.0.479 (#12103)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-07 16:01:10 +00:00
github-actions[bot]andKoenkk 6ee7f56df3 Update zigbee-herdsman-converters to 14.0.478 (#12094)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-06 15:38:55 +00:00
github-actions[bot]andKoenkk d7073409d4 Update zigbee-herdsman to 0.14.24 (#12092)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-06 15:27:58 +00:00
github-actions[bot]andKoenkk e46ddb8c79 Update zigbee-herdsman-converters to 14.0.477 (#12091)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-06 15:12:18 +00:00
github-actions[bot]andKoenkk f91d0f6bfe Update zigbee-herdsman-converters to 14.0.476 (#12084)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-05 18:21:15 +00:00
github-actions[bot]andKoenkk 7e61252d11 Update zigbee-herdsman-converters to 14.0.475 (#12069)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-04 18:26:35 +00:00
github-actions[bot]andKoenkk 67c5481965 Update zigbee-herdsman-converters to 14.0.474 (#12068)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-04 18:22:54 +00:00
github-actions[bot]andKoenkk e251fc0803 Update zigbee-herdsman-converters to 14.0.473 (#12060)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-03 19:27:11 +00:00
Koen Kanters 5d4268515c Fix garage door status incorrect in Home Assistant. https://github.com/Koenkk/zigbee2mqtt/issues/12038 2022-04-03 21:09:26 +02:00
github-actions[bot]andKoenkk 91d80f1bb0 Update zigbee-herdsman-converters to 14.0.472 (#12056)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-03 12:14:53 +00:00
github-actions[bot]andKoenkk c9fbc6d52e Update zigbee-herdsman to 0.14.23 (#12055)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-03 12:08:18 +00:00
github-actions[bot]andKoenkk b84ad454e5 Update zigbee-herdsman-converters to 14.0.471 (#12049)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-03 07:31:13 +00:00
github-actions[bot]andKoenkk 51ae801cb8 Update dependencies (#12047)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-03 08:49:18 +02:00
Koen Kanters cabef43421 Revert "Switch to package-lock.json"
This reverts commit 0163b7281a.
2022-04-03 08:49:03 +02:00
Koen Kanters 8fd8df75be Reduce Docker image size again. 2022-04-02 21:50:12 +02:00
Koen Kanters 51f8c1c487 Fix Docker build 2022-04-02 21:13:22 +02:00
Koen Kanters aeb9927c20 Reduce Docker image build time 2022-04-02 21:09:02 +02:00
Koen Kanters 0163b7281a Switch to package-lock.json 2022-04-02 20:57:44 +02:00
Koen Kanters 0471eb5501 Switch to package-lock.json 2022-04-02 20:51:32 +02:00
2679219d87 Update zigbee-herdsman to 0.14.22 (#12046)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-04-02 18:47:51 +00:00
Koen Kanters d83eb72c4b Add registry url 2022-04-02 20:44:16 +02:00
Koen Kanters 414ea10b14 Update actions 2022-04-02 20:41:18 +02:00
github-actions[bot]andKoenkk d6e29538b1 Update zigbee-herdsman-converters to 14.0.470 (#12045)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-02 18:38:36 +00:00
Koen Kanters eb9b764cc5 Anoter attempt to fix shrinkwrap 2022-04-02 20:25:02 +02:00
Koen Kanters db642832f8 Update shrinkwrap 2022-04-02 19:59:40 +02:00
github-actions[bot]andKoenkk a56e5e0f36 Update zigbee-herdsman to 0.14.21 (#12043)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-04-02 17:58:11 +00:00
Koen Kanters 1b0fbf556b Fix invalid Home Assistant device class for garage_door_contact. #12038 2022-04-02 19:31:32 +02:00
Artem Draft 3e7eddf3ce Improve handle motor_state in HA (#12028) 2022-04-02 19:17:36 +02:00
Koen Kanters ad24c4f677 Promote to dev 2022-04-01 08:28:18 +02:00
Koen Kanters 6f1460e47b 1.25.0 2022-04-01 08:26:35 +02:00
Koen Kanters 91239291ec Undev 2022-04-01 08:26:33 +02:00
Koen Kanters beccc18ba7 Merge branch 'master' into dev 2022-03-31 22:02:47 +02:00
github-actions[bot]andKoenkk 2901df37fb Update zigbee-herdsman-converters to 14.0.467 (#12008)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-31 19:28:24 +00:00
github-actions[bot]andnurikk 1c1ca6faea Update zigbee2mqtt-frontend to 0.6.80 (#12001)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-03-31 07:49:42 +00:00
github-actions[bot]andKoenkk 85308daf5f Update zigbee-herdsman-converters to 14.0.466 (#11991)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-30 15:31:23 +00:00
github-actions[bot]andKoenkk 4631f6fc22 Update zigbee-herdsman-converters to 14.0.465 (#11984)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-29 19:05:17 +00:00
github-actions[bot]andKoenkk 95c698758d Update zigbee-herdsman-converters to 14.0.464 (#11974)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-28 18:32:35 +00:00
github-actions[bot]andKoenkk 95c48309a0 Update dependencies (#11962)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-27 09:55:27 +02:00
github-actions[bot]andKoenkk 2e42f50d6a Update zigbee-herdsman-converters to 14.0.463 (#11961)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-26 22:05:47 +00:00
github-actions[bot]andKoenkk 62a7b5115c Update zigbee-herdsman-converters to 14.0.462 (#11959)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-26 14:01:19 +00:00
github-actions[bot]andKoenkk b805dadceb Update zigbee-herdsman-converters to 14.0.461 (#11947)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-24 18:18:15 +00:00
Artem Draft f95c0bb048 Handle cover motor_state in HA (#11926)
* Handle cover motor_state in HA

* lint
2022-03-23 17:19:06 +01:00
github-actions[bot]andKoenkk 0202399ff6 Update zigbee-herdsman-converters to 14.0.460 (#11927)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-22 18:44:49 +00:00
github-actions[bot]andKoenkk e4d4f04a05 Update zigbee-herdsman-converters to 14.0.459 (#11920)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-21 16:39:48 +00:00
github-actions[bot]andKoenkk efe768d24a Update zigbee-herdsman-converters to 14.0.458 (#11912)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-20 15:51:12 +00:00
github-actions[bot]andKoenkk 0a868b451f Update dependencies (#11906)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-20 16:12:41 +01:00
Koen Kanters a73caa3e66 Update issue template 2022-03-19 16:44:09 +01:00
Koen Kanters 02bc325fc2 Fix configure failing when rejoining device. #10973 2022-03-19 16:41:22 +01:00
github-actions[bot]andKoenkk 847e7f8652 Update zigbee-herdsman-converters to 14.0.457 (#11901)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-19 14:22:43 +00:00
github-actions[bot]andKoenkk 0cd02c7495 Update zigbee-herdsman-converters to 14.0.456 (#11898)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-18 16:11:22 +00:00
github-actions[bot]andKoenkk cae83c90b9 Update zigbee-herdsman-converters to 14.0.455 (#11888)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-17 18:33:40 +00:00
Koen Kanters 2524e030fa Fix device reconnect resulting in brightness 1 when device is off. #11864 2022-03-17 19:21:35 +01:00
github-actions[bot]andKoenkk 652d4018dd Update zigbee-herdsman-converters to 14.0.454 (#11877)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-16 19:28:58 +00:00
github-actions[bot]andKoenkk 5b730575e4 Update zigbee-herdsman-converters to 14.0.452 (#11857)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-15 16:00:14 +00:00
github-actions[bot]andKoenkk 83f8250358 Update zigbee-herdsman-converters to 14.0.451 (#11845)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-14 17:12:06 +00:00
Koen Kanters 8bf06901c2 Availability for groups (#11626)
* Availability for groups. https://github.com/Koenkk/zigbee2mqtt/issues/9176

* Updates

* Fix
2022-03-14 15:51:02 +01:00
github-actions[bot]andKoenkk 4a0cd427de Update zigbee-herdsman-converters to 14.0.450 (#11839)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-13 18:20:54 +00:00
github-actions[bot]andKoenkk 23aacc4c00 Update dependencies (#11824)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-13 18:51:28 +01:00
github-actions[bot]andnurikk e7aef4c208 Update zigbee2mqtt-frontend to 0.6.79 (#11833)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-03-13 13:27:05 +00:00
github-actions[bot]andKoenkk 3cf3366070 Update zigbee-herdsman-converters to 14.0.449 (#11814)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-12 09:21:24 +00:00
Artem Draft 8f1787ed4a Expose cover running state to Home Assistant (#11789)
* handle cover running state in HA

* add running if the expose

* find running in definitionExposes
2022-03-12 10:14:25 +01:00
Koen Kanters b515c3213f Node 16 (#11804)
* Node 16

* More Node 16
2022-03-11 20:39:25 +01:00
github-actions[bot]andKoenkk 1aea3194bb Update zigbee-herdsman-converters to 14.0.448 (#11801)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-11 16:18:36 +00:00
github-actions[bot]andKoenkk 8f98a2f6d0 Update zigbee-herdsman-converters to 14.0.447 (#11787)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-10 17:27:39 +00:00
github-actions[bot]andKoenkk 52d151e70f Update zigbee-herdsman-converters to 14.0.446 (#11764)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-08 18:34:43 +00:00
github-actions[bot]andKoenkk 42f229b19e Update zigbee-herdsman-converters to 14.0.445 (#11753)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-07 17:52:33 +00:00
Koen Kanters e5d6bd1d68 Don't cache no_occupancy_since. https://github.com/Koenkk/zigbee2mqtt/issues/11183 2022-03-07 18:38:36 +01:00
github-actions[bot]andKoenkk c49f546256 Update zigbee-herdsman-converters to 14.0.444 (#11737)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-06 20:41:29 +00:00
github-actions[bot]andKoenkk 141f9f6d04 Update dependencies (#11726)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-06 20:13:20 +01:00
github-actions[bot]andnurikk a73a5b2cd4 Update zigbee2mqtt-frontend to 0.6.78 (#11733)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-03-06 08:39:44 +00:00
github-actions[bot]andKoenkk f7c6207d4f Update zigbee-herdsman-converters to 14.0.443 (#11716)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-05 15:36:48 +00:00
github-actions[bot]andKoenkk 9592147972 Update zigbee-herdsman-converters to 14.0.442 (#11696)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-03 17:24:06 +00:00
github-actions[bot]andnurikk a1cc8049b1 Update zigbee2mqtt-frontend to 0.6.77 (#11691)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-03-03 14:57:17 +00:00
st4ro 625236721f new device class GARAGE for garage door opener (#11680) 2022-03-02 21:31:15 +01:00
Andrew Berry 364a6dd2aa Only colorize output if logging to a tty (#11645) 2022-03-02 20:07:22 +01:00
github-actions[bot]andKoenkk 3535b2c6c2 Update zigbee-herdsman-converters to 14.0.439 (#11662)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-01 18:44:56 +00:00
Koen Kanters 91d597e53d Dev 2022-03-01 17:03:45 +01:00
Koen Kanters 7a2ddf24d3 1.24.0 2022-03-01 17:02:51 +01:00
Koen Kanters d123ea3e61 Undev 2022-03-01 17:02:10 +01:00
github-actions[bot]andKoenkk eda6e7ca37 Update zigbee-herdsman-converters to 14.0.438 (#11660)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-03-01 16:01:10 +00:00
Koen Kanters b50e40c9ca Update generateNewSupportedDevices.js 2022-03-01 16:50:26 +01:00
github-actions[bot]andnurikk 1eb3287029 Update zigbee2mqtt-frontend to 0.6.76 (#11656)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-03-01 03:45:41 +00:00
github-actions[bot]andKoenkk bc8cd8df94 Update zigbee-herdsman-converters to 14.0.437 (#11655)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-28 17:07:31 +00:00
github-actions[bot]andKoenkk 8751eca171 Update zigbee-herdsman-converters to 14.0.436 (#11634)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-27 12:57:56 +00:00
github-actions[bot]andKoenkk 107b2726b2 Update dependencies (#11630)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-27 13:38:54 +01:00
github-actions[bot]andKoenkk 987205c004 Update zigbee-herdsman-converters to 14.0.435 (#11624)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-26 11:55:57 +00:00
github-actions[bot]andKoenkk 4f7ee6898a Update zigbee-herdsman-converters to 14.0.434 (#11620)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-25 19:12:14 +00:00
github-actions[bot]andKoenkk 67ffcebe69 Update zigbee-herdsman-converters to 14.0.433 (#11614)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-25 16:16:56 +00:00
github-actions[bot]andKoenkk afe94a7cba Update zigbee-herdsman-converters to 14.0.432 (#11596)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-24 16:10:04 +00:00
a5db6ef0c4 Update zigbee-herdsman-converters to 14.0.431 (#11585)
* Update zigbee-herdsman-converters to 14.0.431

* Fix tests

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-02-23 18:59:33 +00:00
github-actions[bot]andKoenkk 5dc369c5a4 Update zigbee-herdsman to 0.14.20 (#11584)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-23 18:23:39 +00:00
github-actions[bot]andKoenkk 65bd5423e8 Update zigbee-herdsman to 0.14.19 (#11583)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-23 18:17:16 +00:00
github-actions[bot]andKoenkk 9c33c90650 Update zigbee-herdsman-converters to 14.0.430 (#11569)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-22 15:54:08 +00:00
github-actions[bot]andKoenkk caafc64fbf Update zigbee-herdsman-converters to 14.0.429 (#11551)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-21 20:56:31 +00:00
github-actions[bot]andKoenkk 9e35f22ac4 Update zigbee-herdsman-converters to 14.0.427 (#11550)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-21 16:51:07 +00:00
github-actions[bot]andnurikk 23b7898a43 Update zigbee2mqtt-frontend to 0.6.75 (#11549)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-02-21 16:45:42 +00:00
github-actions[bot]andKoenkk 0fdbb9153d Update zigbee-herdsman to 0.14.18 (#11546)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-21 16:31:22 +00:00
github-actions[bot]andnurikk a345181812 Update zigbee2mqtt-frontend to 0.6.74 (#11534)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-02-21 01:16:43 +00:00
github-actions[bot]andKoenkk 7f4f48309e Update zigbee-herdsman to 0.14.17 (#11513)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-20 14:28:17 +00:00
github-actions[bot]andKoenkk f1eb605bef Update zigbee-herdsman-converters to 14.0.426 (#11512)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-20 14:01:14 +00:00
TobiasandKoen Kanters 9cfc38223e Add device and groups descriptions (#11493)
* Add device and groups descriptions

* Improvements

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-02-20 14:45:53 +01:00
github-actions[bot]andKoenkk abdb2b20c2 Update dependencies (#11499)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-20 14:22:20 +01:00
github-actions[bot]andnurikk 6d521147fa Update zigbee2mqtt-frontend to 0.6.73 (#11506)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-02-20 12:02:22 +00:00
github-actions[bot]andnurikk df97c79b78 Update zigbee2mqtt-frontend to 0.6.72 (#11504)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-02-20 05:43:33 +00:00
github-actions[bot]andKoenkk f6f507cda0 Update zigbee-herdsman-converters to 14.0.425 (#11486)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-19 09:04:10 +00:00
github-actions[bot]andKoenkk ba1843ff74 Update zigbee-herdsman-converters to 14.0.424 (#11478)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-18 15:33:45 +00:00
github-actions[bot]andKoenkk aace02ccda Update zigbee-herdsman-converters to 14.0.423 (#11457)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-16 20:51:36 +00:00
Koen Kanters 7ec6a2b483 Allow to specify options device/configure_reporting. https://github.com/Koenkk/zigbee2mqtt/issues/11442 2022-02-16 21:32:59 +01:00
84043702c1 Update zigbee-herdsman-converters to 14.0.422 (#11441)
* Update zigbee-herdsman-converters to 14.0.422

* fix tests

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-02-15 19:12:49 +00:00
github-actions[bot]andKoenkk 825308df20 Update zigbee-herdsman to 0.14.16 (#11440)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-15 19:03:43 +00:00
github-actions[bot]andKoenkk e0694a444e Update zigbee-herdsman to 0.14.15 (#11438)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-15 18:48:02 +00:00
github-actions[bot]andKoenkk f284a5d2d5 Update zigbee-herdsman-converters to 14.0.421 (#11402)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-13 14:46:03 +00:00
github-actions[bot]andKoenkk 5849a90030 Update dependencies (#11396)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-13 15:03:23 +01:00
github-actions[bot]andKoenkk b518e3e200 Update zigbee-herdsman-converters to 14.0.420 (#11389)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-12 16:09:56 +00:00
github-actions[bot]andKoenkk bccd7b390c Update zigbee-herdsman-converters to 14.0.419 (#11380)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-12 09:52:42 +00:00
4933b9080e Update zigbee-herdsman-converters to 14.0.418 (#11366)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-02-11 18:25:00 +00:00
github-actions[bot]andKoenkk cd9fd2d5c2 Update zigbee-herdsman to 0.14.14 (#11365)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-11 18:00:39 +00:00
Koen Kanters 9602574cd2 Add winston-transport dependency. https://github.com/Koenkk/zigbee2mqtt/issues/11356 2022-02-11 18:52:00 +01:00
github-actions[bot]andKoenkk 711e2d9cfc Update zigbee-herdsman-converters to 14.0.417 (#11346)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-10 18:46:56 +00:00
github-actions[bot]andKoenkk d8ecc6f26e Update zigbee-herdsman-converters to 14.0.416 (#11331)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-09 18:26:33 +00:00
github-actions[bot]andKoenkk 7fe803a28c Update zigbee-herdsman-converters to 14.0.415 (#11310)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-08 19:03:03 +00:00
6394d0f42e Update zigbee-herdsman-converters to 14.0.414 (#11284)
* Update zigbee-herdsman-converters to 14.0.414

* Fix tests

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-02-07 17:11:04 +00:00
github-actions[bot]andKoenkk 52607d03cf Update zigbee-herdsman to 0.14.13 (#11275)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-07 09:13:59 +00:00
github-actions[bot]andKoenkk 5dd8873fcd Update zigbee-herdsman-converters to 14.0.413 (#11266)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-06 19:52:50 +00:00
github-actions[bot]andKoenkk 02e18b9d5b Update zigbee-herdsman-converters to 14.0.412 (#11258)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-06 13:26:16 +00:00
github-actions[bot]andKoenkk 42bb73e263 Update dependencies (#11254)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-06 14:11:24 +01:00
Ryan Kim(True-World) 39bf36b0b5 delete device class of alram(enum) (#11236) 2022-02-06 13:50:55 +01:00
github-actions[bot]andKoenkk b5e8566f58 Update zigbee-herdsman-converters to 14.0.411 (#11233)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-05 09:39:34 +00:00
Ryan Kim(True-World)andKoen Kanters 21567ab810 Add more Home Assistant device classes (#11225)
* add some device class

device class.

* fix lint

* Update homeassistant.ts

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-02-05 09:20:35 +00:00
ValentinandKoen Kanters f9c4b4e926 HA Discover when user change option (#11210)
* HA Discover when user change option

* Fix unit tests

* Update homeassistant.ts

* Update homeassistant.ts

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-02-05 10:12:13 +01:00
github-actions[bot]andKoenkk 8357da270d Update zigbee-herdsman to 0.14.12 (#11228)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-04 17:31:55 +00:00
github-actions[bot]andKoenkk 4def85b3ca Update zigbee-herdsman-converters to 14.0.410 (#11227)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-04 17:24:23 +00:00
Koen Kanters 0e11ae07d9 Recompute device definition when device changes modelID. https://github.com/Koenkk/zigbee-herdsman-converters/issues/3016 2022-02-04 18:02:47 +01:00
github-actions[bot]andKoenkk 9fb1eb40d3 Update zigbee-herdsman-converters to 14.0.409 (#11209)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-03 16:17:09 +00:00
github-actions[bot]andKoenkk ddcb8e12d5 Update zigbee-herdsman-converters to 14.0.408 (#11196)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-02 19:01:18 +00:00
github-actions[bot]andKoenkk 3470681ea7 Update zigbee-herdsman-converters to 14.0.407 (#11195)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-02 18:52:28 +00:00
Koen Kanters 41ece365aa Allow to configure availablility payloads to be a JSON object. #10802 2022-02-02 19:28:44 +01:00
Koen Kanters 73f02bfee3 Fix crash when availability is enabled per device. #11184 2022-02-02 17:39:45 +01:00
github-actions[bot]andKoenkk fae8e96229 Update zigbee-herdsman to 0.14.11 (#11185)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-02 10:40:49 +00:00
Koen Kanters 7f9b198722 Promote to dev 2022-02-01 19:13:28 +01:00
Koen Kanters 6ea61bae2c 1.23.0 2022-02-01 19:12:56 +01:00
Koen Kanters 5c915e5fd7 Undev 2022-02-01 19:12:11 +01:00
Koen Kanters b1f50701b2 Merge branch 'dev' 2022-02-01 19:11:47 +01:00
github-actions[bot]andKoenkk 273fc61099 Update zigbee-herdsman-converters to 14.0.406 (#11168)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-02-01 16:08:31 +00:00
github-actions[bot]andKoenkk 59ef38f076 Update zigbee-herdsman-converters to 14.0.405 (#11151)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-31 16:59:43 +00:00
Steven RoebertandKoen Kanters 9171ef35da Allowing to use HA device name override as base for entities (#11107)
* When overriding the home assistant name, use that name as the device name and as a base for each entity name

* Update homeassistant.ts

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-01-31 17:46:42 +01:00
Koen Kanters 912360c1f0 Fix availability passive device timeout config. https://github.com/Koenkk/zigbee2mqtt/pull/10437 2022-01-31 17:28:08 +01:00
github-actions[bot]andKoenkk 04290274b8 Update zigbee-herdsman to 0.14.10 (#11150)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-31 15:13:10 +00:00
github-actions[bot]andKoenkk 9fc413dea9 Update zigbee-herdsman to 0.14.9 (#11147)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-31 10:26:31 +00:00
github-actions[bot]andnurikk 7863f600c8 Update zigbee2mqtt-frontend to 0.6.71 (#11130)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-01-30 17:19:43 +00:00
github-actions[bot]andnurikk 0bdbf7194b Update zigbee2mqtt-frontend to 0.6.70 (#11122)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-01-30 15:07:02 +00:00
github-actions[bot]andKoenkk 81131c32a8 Update zigbee-herdsman-converters to 14.0.404 (#11120)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-30 13:27:41 +00:00
github-actions[bot]andKoenkk 26ae01a0e4 Update dependencies (#11108)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-30 14:12:07 +01:00
Ilya Kirov 93921c6be9 Fix endpoint match (again) (#11083)
https://github.com/Koenkk/zigbee2mqtt/pull/10898
https://github.com/Koenkk/zigbee2mqtt/issues/11068
2022-01-29 09:58:23 +01:00
github-actions[bot]andKoenkk 2721cc1bbf Update zigbee-herdsman-converters to 14.0.403 (#11078)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-28 16:32:45 +00:00
github-actions[bot]andKoenkk 9120ccf403 Update zigbee-herdsman to 0.14.8 (#11077)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-28 16:26:37 +00:00
Ilya Kirov@RubenKelevra <cyrond@gmail.com>Koen Kanters
b92131cad6 Revert "Fix endpoint match" (#11072)
* GitHub issue templates: : Remove leading "[Problem]" in the title and the requirements to fill out all fields (#10779)

* remove leading "[Problem] " in the title

We already set a tag for this

* remove the need to enter all field in a bug report

there are certain cases where this isn't needed and filling them out with blank texts just to remove the text with an edit after filing feels dumb.

* Revert "Fix endpoint match (#10898)"

This reverts commit 5b918d9e3474c313878ff4275b551f79f39c85e2.

Co-authored-by: @RubenKelevra <cyrond@gmail.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-01-28 15:45:05 +00:00
github-actions[bot]andKoenkk 0b06483ce0 Update zigbee-herdsman-converters to 14.0.402 (#11063)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-27 19:00:39 +00:00
github-actions[bot]andKoenkk 6a4fba82df Update zigbee-herdsman-converters to 14.0.401 (#11060)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-27 16:09:43 +00:00
github-actions[bot]andKoenkk 1a2943d45e Update zigbee-herdsman-converters to 14.0.400 (#11030)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-26 16:52:47 +00:00
Joshua SpenceandKoen Kanters 1a70b80282 Flush logger before exiting process (#10905)
* Wait for async logs

* Fix tests

* Better solution

Based on https://stackoverflow.com/a/59260151

* Fix lint/test coverage

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-01-26 15:57:51 +00:00
github-actions[bot]andKoenkk c4c02d2e2d Update zigbee-herdsman-converters to 14.0.399 (#11011)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-25 16:06:24 +00:00
github-actions[bot]andKoenkk 5b9b7e4804 Update zigbee-herdsman to 0.14.7 (#11003)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-25 11:33:24 +00:00
github-actions[bot]andKoenkk 8e8c3d7b49 Update zigbee-herdsman-converters to 14.0.398 (#10996)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-24 15:59:58 +00:00
github-actions[bot]andKoenkk f0f7662f9e Update zigbee-herdsman-converters to 14.0.397 (#10987)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-23 20:17:22 +00:00
github-actions[bot]andKoenkk 5ef7b673d8 Update zigbee-herdsman to 0.14.6 (#10985)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-23 20:05:07 +00:00
github-actions[bot]andnurikk b6d26e351c Update zigbee2mqtt-frontend to 0.6.69 (#10976)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-01-23 16:56:05 +00:00
github-actions[bot]andKoenkk a10306b247 Update zigbee-herdsman-converters to 14.0.396 (#10970)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-23 13:36:08 +00:00
github-actions[bot]andKoenkk e7e23a1092 Update dependencies (#10961)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-23 13:25:38 +01:00
github-actions[bot]andKoenkk d5de1c6248 Update zigbee-herdsman to 0.14.5 (#10950)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-22 14:12:23 +00:00
github-actions[bot]andKoenkk 14dd4800f1 Update zigbee-herdsman-converters to 14.0.395 (#10949)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-22 13:56:37 +00:00
Koen Kanters 3451c71a21 Updates for https://github.com/Koenkk/zigbee-herdsman-converters/pull/3708 2022-01-22 10:54:01 +01:00
Koen Kanters 8e72b3bf64 Add preferredColorMode option. https://github.com/Koenkk/zigbee-herdsman-converters/pull/3708 2022-01-22 08:48:37 +01:00
github-actions[bot]andKoenkk 3ba185815f Update zigbee-herdsman-converters to 14.0.394 (#10941)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-21 19:39:06 +00:00
Koen Kanters 64f83d74b7 Clear old availability topic when device is renamed. #5244 2022-01-21 20:21:18 +01:00
github-actions[bot]andKoenkk ef2f28019b Update zigbee-herdsman to 0.14.4 (#10939)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-21 19:11:48 +00:00
github-actions[bot]andKoenkk e446c81677 Update zigbee-herdsman-converters to 14.0.393 (#10917)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-20 19:09:12 +00:00
Ilya Kirov d87347923b Fix endpoint match (#10898)
https://github.com/Koenkk/zigbee2mqtt/discussions/9504

For this case topic is 'state_top_left', 'state_bottom_left' and etc..,
Old pattern it is match as: [state_top_left, state_top, left]
New pattern it is match as: [state_top_left, state, top_left]
2022-01-19 22:15:23 +01:00
github-actions[bot]andKoenkk a867b719dc Update zigbee-herdsman-converters to 14.0.392 (#10895)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-19 17:00:46 +00:00
github-actions[bot]andKoenkk da3817bd40 Update zigbee-herdsman to 0.14.3 (#10893)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-19 16:36:11 +00:00
github-actions[bot]andKoenkk 7f98caca22 Update zigbee-herdsman to 0.14.2 (#10887)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-19 07:39:24 +00:00
github-actions[bot]andKoenkk 107ac3659f Update zigbee-herdsman-converters to 14.0.391 (#10876)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-18 16:25:25 +00:00
github-actions[bot]andKoenkk 48dfd581c1 Update zigbee-herdsman-converters to 14.0.390 (#10859)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-17 17:07:52 +00:00
github-actions[bot]andKoenkk 8e53d745cd Update zigbee-herdsman-converters to 14.0.389 (#10840)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-16 21:50:06 +00:00
github-actions[bot]andKoenkk 6877c591b8 Update zigbee-herdsman to 0.14.1 (#10839)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-16 21:44:33 +00:00
Koen Kanters d0ceaba6f2 Fix OTA (#10833)
* dump

* updates

* Updates
2022-01-16 18:25:53 +01:00
0d60777565 Update zigbee-herdsman-converters to 14.0.388 (#10838)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-01-16 18:16:10 +01:00
github-actions[bot]andKoenkk 074c70be54 Update zigbee-herdsman to 0.14.0 (#10837)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-16 18:15:06 +01:00
Koen Kanters b6a7ade6f2 Publish bridge/state online when Home Assistant comes online. https://github.com/Koenkk/zigbee2mqtt/issues/9629 2022-01-16 14:56:58 +01:00
github-actions[bot]andKoenkk d6ee1a6450 Update zigbee-herdsman-converters to 14.0.387 (#10827)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-16 08:59:30 +00:00
github-actions[bot]andKoenkk 3617ba2b1d Update dependencies (#10826)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-16 09:11:18 +01:00
github-actions[bot]andKoenkk ec8e7f57fa Update zigbee-herdsman-converters to 14.0.386 (#10821)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-15 19:14:46 +00:00
github-actions[bot]andKoenkk 87d33febcf Update zigbee-herdsman-converters to 14.0.385 (#10803)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-15 10:20:46 +00:00
Valentin 43e7ca7ad6 Expose optional user settings to configure() (#10791) 2022-01-14 16:53:22 +01:00
github-actions[bot]andKoenkk 4fd98c2aca Update zigbee-herdsman-converters to 14.0.384 (#10790)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-14 15:35:44 +00:00
@RubenKelevra 1a0a9a6eab GitHub issue templates: : Remove leading "[Problem]" in the title and the requirements to fill out all fields (#10779)
* remove leading "[Problem] " in the title

We already set a tag for this

* remove the need to enter all field in a bug report

there are certain cases where this isn't needed and filling them out with blank texts just to remove the text with an edit after filing feels dumb.
2022-01-14 15:59:10 +01:00
github-actions[bot]andKoenkk 57852fcddf Update zigbee-herdsman-converters to 14.0.383 (#10769)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-13 18:48:27 +00:00
c1b0a13e10 Update zigbee-herdsman to 0.13.194 (#10739)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-01-12 18:20:42 +00:00
github-actions[bot]andKoenkk 3835b792f0 Update zigbee-herdsman-converters to 14.0.382 (#10738)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-12 18:15:43 +00:00
github-actions[bot]andKoenkk 00124215a8 Update dependencies (#10723)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-11 18:32:11 +00:00
github-actions[bot]andKoenkk cac384c3fb Update zigbee-herdsman to 0.13.193 (#10699)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-10 18:33:00 +00:00
github-actions[bot]andKoenkk 0b3727d296 Update zigbee-herdsman-converters to 14.0.380 (#10696)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-10 17:59:01 +00:00
Koen Kanters 30177b0db4 Restructure settings (#10437)
* -

* deep copy schema

* -

* -

* -

* -

* -

* -

* -

* -

* -

* -

* -

* -
2022-01-09 22:28:44 +01:00
Koen Kanters ee6b035108 Support homeassistant_rename for groups. https://github.com/Koenkk/zigbee2mqtt/discussions/10672 2022-01-09 20:39:15 +01:00
Oleksandr MasliuchenkoandKoen Kanters 6ac8a6237e Add support for local OTA firmware images (#10676)
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-01-09 20:38:58 +01:00
github-actions[bot]andKoenkk 44ee2f5f2b Update zigbee-herdsman-converters to 14.0.379 (#10684)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-09 19:29:16 +00:00
github-actions[bot]andKoenkk 9d6009ec99 Update zigbee-herdsman-converters to 14.0.378 (#10669)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-09 09:46:42 +00:00
github-actions[bot]andKoenkk cd60eb5069 Update zigbee-herdsman to 0.13.192 (#10668)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-09 08:48:46 +00:00
github-actions[bot]andKoenkk a10687ecaa Update dependencies (#10667)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-09 09:26:41 +01:00
github-actions[bot]andKoenkk 89693df2e1 Update zigbee-herdsman to 0.13.191 (#10659)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-08 16:30:44 +00:00
Koen Kanters e783117a3a Xiaomi retransmitted messages handling has been moved to zigbee-herdsman. https://github.com/Koenkk/zigbee2mqtt/issues/10656 2022-01-08 17:25:20 +01:00
github-actions[bot]andKoenkk e6b041436a Update zigbee-herdsman-converters to 14.0.376 (#10654)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-08 14:38:40 +00:00
github-actions[bot]andKoenkk 3d4d7dc910 Update zigbee-herdsman-converters to 14.0.375 (#10650)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-07 22:21:24 +00:00
github-actions[bot]andKoenkk 811ef1dcb5 Update zigbee-herdsman-converters to 14.0.374 (#10646)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-07 16:31:15 +00:00
github-actions[bot]andKoenkk 208a552c02 Update zigbee-herdsman-converters to 14.0.373 (#10628)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-06 20:57:04 +00:00
github-actions[bot]andKoenkk 74cf20b172 Update zigbee-herdsman-converters to 14.0.372 (#10622)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-06 17:34:01 +00:00
Koen Kanters dbcd768e1d Log out of memory error when initial build fails. #10555 2022-01-06 17:59:10 +01:00
github-actions[bot]andKoenkk 43ded111fb Update zigbee-herdsman-converters to 14.0.371 (#10600)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-05 18:56:05 +00:00
Oleksandr MasliuchenkoandKoen Kanters 16842a3e72 Add posibility to have local OTA index (#10141)
* Add a configuration option to override OTA index file

* Cleanup

* Code review corrections

* Move zigbee_ota_override_index_location to ota section of the configuration file

* Suport both local and remote (HTTP) OTA override index files

* Updates

* updates

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-01-04 20:29:17 +01:00
Koen Kanters e88ceeec2e Merge remote-tracking branch 'origin/master' into dev 2022-01-04 20:23:20 +01:00
github-actions[bot]andKoenkk f51a7883fe Update zigbee-herdsman-converters to 14.0.370 (#10570)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-04 19:17:53 +00:00
github-actions[bot]andnurikk 4f887a1a3b Update zigbee2mqtt-frontend to 0.6.68 (#10539)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2022-01-03 15:38:54 +00:00
github-actions[bot]andKoenkk 23bdc18608 Update zigbee-herdsman-converters to 14.0.369 (#10523)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-02 21:42:02 +00:00
github-actions[bot]andKoenkk 101e0192bd Update zigbee-herdsman to 0.13.190 (#10522)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-02 21:30:24 +00:00
github-actions[bot]andKoenkk 24d9065d26 Update zigbee-herdsman-converters to 14.0.368 (#10521)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-02 21:07:03 +00:00
05d545945d Update zigbee-herdsman-converters to 14.0.367 (#10502)
* Update zigbee-herdsman-converters to 14.0.367

* Update publish.test.js

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-01-02 12:54:30 +00:00
github-actions[bot]andKoenkk 089ff8f370 Update zigbee-herdsman to 0.13.189 (#10503)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-02 10:39:13 +00:00
Koen Kanters 301ba14b00 Update new_device_support.yaml 2022-01-02 10:44:47 +01:00
ValentinandKoen Kanters 9d120d225d Automatically expose entitity as energy when Wh/kWh exists (#10133)
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2022-01-02 09:39:34 +00:00
github-actions[bot]andKoenkk 38b07fb8ca Update dependencies (#10495)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-02 10:33:26 +01:00
Rotzbua e8caddaf63 Fix typo: suppress (#10490)
* Fix typo: suppress

* Fix typo: discovered
2022-01-02 07:11:39 +00:00
Koen Kanters 15f1df8fa4 Promote to dev 2022-01-01 10:47:30 +01:00
Koen Kanters 414c51f5b2 1.22.2 2022-01-01 10:46:42 +01:00
Koen Kanters 0c42067006 Undev 2022-01-01 10:45:46 +01:00
Koen Kanters 5741904e75 Merge branch 'master' into dev 2022-01-01 10:44:52 +01:00
github-actions[bot]andKoenkk c60fdfd39b Update zigbee-herdsman-converters to 14.0.366 (#10472)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-01-01 09:37:27 +00:00
Koen Kanters a7682498ef Fix set message with endpoint in payload where the endpoint name ends with a number not working. #10423 2021-12-31 14:05:15 +01:00
github-actions[bot]andnurikk c6a3b90c2f Update zigbee2mqtt-frontend to 0.6.67 (#10466)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-31 12:17:44 +00:00
github-actions[bot]andKoenkk ebd35052c0 Update zigbee-herdsman-converters to 14.0.365 (#10463)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-31 11:02:13 +00:00
github-actions[bot]andnurikk 2849f30417 Update zigbee2mqtt-frontend to 0.6.66 (#10458)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-31 06:04:35 +00:00
Koen Kanters e8526b7b58 Better test for #10355 2021-12-30 17:32:02 +01:00
github-actions[bot]andKoenkk 3166d37096 Update zigbee-herdsman-converters to 14.0.364 (#10446)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-30 14:43:59 +00:00
github-actions[bot]andKoenkk 2d2c01981e Update zigbee-herdsman-converters to 14.0.363 (#10441)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-30 10:37:06 +00:00
58b702e8ef Update zigbee-herdsman to 0.13.188 (#10440)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-12-30 10:11:08 +00:00
github-actions[bot]andKoenkk 6efa20fa1d Update zigbee-herdsman-converters to 14.0.362 (#10439)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-30 10:06:22 +00:00
github-actions[bot]andKoenkk bf72347468 Update zigbee-herdsman-converters to 14.0.361 (#10433)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-29 18:14:08 +00:00
Ryan Kim(True-World) 73a6ad4cf8 add HA expose, binary sensor card (#10406)
* add binary sensor card

* fix lint
2021-12-29 16:21:59 +01:00
Koen Kanters 1e75dd5628 Test case for #10355 2021-12-27 20:29:16 +01:00
bc92bd73e6 Update zigbee-herdsman to 0.13.187 (#10394)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-12-27 19:08:38 +00:00
github-actions[bot]andKoenkk 8756367666 Update zigbee-herdsman-converters to 14.0.360 (#10393)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-27 19:03:17 +00:00
Alex CherkayevandOleksandr Cherkayev 79e5f0844a Add 4417 to bind poll (#10379)
Co-authored-by: Oleksandr Cherkayev <ocherkayev@luxoft.com>
2021-12-27 18:11:21 +01:00
github-actions[bot]andKoenkk b0585172f4 Update zigbee-herdsman-converters to 14.0.359 (#10369)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-26 10:28:37 +00:00
Koen Kanters 89996835ed Update new device support template 2021-12-26 11:21:52 +01:00
Koen Kanters 4f2a1e55e5 Update new device support template 2021-12-26 11:18:56 +01:00
Koen Kanters d488563f2a Update new device support template 2021-12-26 11:18:10 +01:00
Koen Kanters 4310171abc Update new device support template 2021-12-26 11:16:14 +01:00
Koen Kanters 36d7b4b022 Update new device support template 2021-12-26 11:15:11 +01:00
Koen Kanters 50a6ed14d4 Allow read/write to unsupported devices. 2021-12-26 10:54:39 +01:00
github-actions[bot]andKoenkk ee623c2484 Update dependencies (#10362)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-26 10:10:22 +01:00
github-actions[bot]andKoenkk 2e6588719b Update zigbee-herdsman-converters to 14.0.358 (#10356)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-25 15:00:19 +00:00
github-actions[bot]andnurikk fb35b52ca6 Update zigbee2mqtt-frontend to 0.6.65 (#10349)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-25 04:43:38 +00:00
github-actions[bot]andKoenkk 3306b63299 Update zigbee-herdsman-converters to 14.0.357 (#10345)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-24 19:58:58 +00:00
Koen Kanters 7a4ecadb6a Change coordinator ieee addr place. 2021-12-24 17:56:18 +01:00
Koen Kanters 511f3b09af Expose coordinator_ieeeaddr on bridge/info 2021-12-24 17:52:30 +01:00
github-actions[bot]andnurikk 828982d36c Update zigbee2mqtt-frontend to 0.6.64 (#10344)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-24 15:37:18 +00:00
github-actions[bot]andKoenkk 61bd95f8b8 Update zigbee-herdsman to 0.13.186 (#10338)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-23 20:27:48 +00:00
ValentinandKoen Kanters 74cff6a983 Call onEvent when device options change (#10322)
* Notify when options change

* publish device info after options change

* Refactor

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-12-23 21:18:27 +01:00
Koen Kanters fdf7d10a41 Update problem report template 2021-12-23 21:12:06 +01:00
Koen Kanters 1145e4b81b Update 2021-12-23 21:08:31 +01:00
Koen Kanters 93f01d545e Fix issue templates 2021-12-23 21:04:38 +01:00
Koen Kanters 30e7385cac Update issue templates 2021-12-23 21:02:15 +01:00
Koen Kanters 513314963c Update issue template 2021-12-23 20:31:14 +01:00
Koen Kanters 3c0cd8ebfa Fix issue template syntax 2021-12-23 20:27:00 +01:00
Koen Kanters 5bedc044d6 Update issue templates 2021-12-23 20:24:06 +01:00
2a8a98f9a3 Update zigbee-herdsman-converters to 14.0.356 (#10329)
* Update zigbee-herdsman-converters to 14.0.356

* Fix tests

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-12-23 19:00:19 +00:00
github-actions[bot]andKoenkk 03f24a63d5 Update zigbee-herdsman to 0.13.185 (#10330)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-23 18:46:45 +00:00
53003b3d0d Update zigbee-herdsman-converters to 14.0.355 (#10317)
* Update zigbee-herdsman-converters to 14.0.355

* Fix tests

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-12-22 20:34:24 +00:00
ValentinandKoen Kanters cd5dd39971 Dynamically expose attributes (#10132)
* Dynamically expose attributes

* Rollback options in configure step

Use event instead, as proposed in https://github.com/Koenkk/zigbee2mqtt/pull/10132/files#r771801648

* Simplify code

* Switch to already existing event

* Fix all unit tests

* Remove redundant code

* Perfect rollback

* Fix typescript types

* Updates

* Updates

* Updates

* updates

* Improve

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-12-22 19:31:41 +00:00
github-actions[bot]andKoenkk 41b150af82 Update zigbee-herdsman-converters to 14.0.354 (#10314)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-22 19:12:34 +00:00
github-actions[bot]andnurikk 3ba1f8cad0 Update zigbee2mqtt-frontend to 0.6.63 (#10311)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-22 14:58:41 +00:00
Koen Kanters f9aa525048 Refactor Home Assistant extension mapping, prepares for #10132 2021-12-21 22:02:05 +01:00
github-actions[bot]andKoenkk 8b4be3d6ad Update zigbee-herdsman-converters to 14.0.353 (#10301)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-21 20:28:49 +00:00
github-actions[bot]andKoenkk 0efe869fcb Update zigbee-herdsman-converters to 14.0.352 (#10277)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-20 20:13:36 +00:00
github-actions[bot]andKoenkk 18cfbae9ca Update zigbee-herdsman-converters to 14.0.351 (#10272)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-20 17:49:39 +00:00
7625d261a5 Update zigbee-herdsman to 0.13.184 (#10247)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-12-19 17:14:25 +00:00
Koen Kanters 6d8eea2142 Fix tests 2021-12-19 18:12:16 +01:00
Koen Kanters 2a9d22e2b3 Software/datacode is now updated by zigbee-herdsman. #10131 2021-12-19 18:00:27 +01:00
github-actions[bot]andKoenkk 656446c828 Update zigbee-herdsman-converters to 14.0.350 (#10239)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-19 10:15:31 +00:00
github-actions[bot]andnurikk f1ffc1d265 Update zigbee2mqtt-frontend to 0.6.62 (#10237)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-19 09:36:53 +00:00
github-actions[bot]andKoenkk 2d1df8f0cb Update dependencies (#10234)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-19 10:28:09 +01:00
github-actions[bot]andKoenkk 253e985388 Update zigbee-herdsman-converters to 14.0.349 (#10220)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-18 09:08:56 +00:00
Koen Kanters 1dd1163288 Fix list not overridable in Home Assistant discovery payload. #10120 2021-12-17 21:37:19 +01:00
github-actions[bot]andKoenkk bb5627d33b Update zigbee-herdsman-converters to 14.0.348 (#10211)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-17 20:28:39 +00:00
github-actions[bot]andKoenkk 38f42bb3c8 Update zigbee-herdsman-converters to 14.0.347 (#10205)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-16 18:34:01 +00:00
github-actions[bot]andKoenkk 5f37145a02 Update zigbee-herdsman-converters to 14.0.346 (#10194)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-15 18:55:43 +00:00
Koen Kanters a56061b36d Attempt to fix crash due to availability state read. #10177 2021-12-15 19:48:32 +01:00
github-actions[bot]andKoenkk b890b1bcb7 Update zigbee-herdsman to 0.13.183 (#10193)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-15 18:33:09 +00:00
Koen Kanters aa84a140e1 Fix null race condition exception. #10108 2021-12-15 19:13:14 +01:00
github-actions[bot]andKoenkk d875b297ad Update zigbee-herdsman to 0.13.182 (#10182)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-14 21:18:08 +00:00
Koen Kanters 1c6ca6519c Delete branch-switcher.yml 2021-12-14 22:14:43 +01:00
github-actions[bot]andKoenkk 900c9f302e Update zigbee-herdsman-converters to 14.0.345 (#10183)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-14 21:13:20 +00:00
Koen Kanters ea04021438 Cleanup availability experimental log. #9907 2021-12-14 20:38:44 +01:00
Koen Kanters 5a624ae188 Fix error for Home Assistant last_seen sensor when epoch is used. #8287 2021-12-14 20:27:49 +01:00
Dmitriy Fishman 42569d6c39 Fix a typo in CONTRIBUTING.md (#10159) 2021-12-14 16:49:23 +01:00
github-actions[bot]andKoenkk 46e06dbbee Update dependencies (#10147)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-14 11:30:45 +01:00
github-actions[bot]andKoenkk a282221e40 Update zigbee-herdsman to 0.13.181 (#10164)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-13 14:30:32 +00:00
github-actions[bot]andnurikk bb22216d09 Update zigbee2mqtt-frontend to 0.6.61 (#10134)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-11 02:54:47 +00:00
github-actions[bot]andnurikk 28cb8b08fb Update zigbee2mqtt-frontend to 0.6.60 (#10122)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-10 04:36:26 +00:00
594a463f8b Update zigbee-herdsman-converters to 14.0.344 (#10116)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-12-09 18:56:19 +00:00
Pavel Sokolov 9c1a15465d Tuya zcl refactor (#10112)
* Rename Tuya command setData->dataRequest
https://github.com/Koenkk/zigbee-herdsman/issues/466

* Fix Tuya datapoint commands invalid format (status,transid -> seq). Tuya has `uint16` type for the sequence(transaction) number.
https://github.com/Koenkk/zigbee-herdsman/issues/466

* Tuya zcl refactoring: fix zigbe2mqtt tests
https://github.com/Koenkk/zigbee-herdsman/issues/466
2021-12-09 19:54:06 +01:00
Koen Kanters 22836f224d Fix filtered attributes being published as null. #10099 2021-12-09 19:52:21 +01:00
github-actions[bot]andKoenkk aafd937f8a Update zigbee-herdsman to 0.13.180 (#10115)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-09 18:39:54 +00:00
github-actions[bot]andKoenkk d0195d0ff9 Update zigbee-herdsman-converters to 14.0.343 (#10092)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-08 17:14:53 +00:00
Koen Kanters 7592c627c2 Fix typo in settings.schema.json. https://github.com/Koenkk/zigbee2mqtt/pull/9803 2021-12-08 17:46:17 +01:00
github-actions[bot]andnurikk 98a57b4540 Update zigbee2mqtt-frontend to 0.6.59 (#10082)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-08 01:50:06 +00:00
github-actions[bot]andKoenkk 70b9d29ddf Update zigbee-herdsman-converters to 14.0.342 (#10079)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-07 21:11:32 +00:00
Koen Kanters ac16d7ce69 Reconfigure device after OTA update. #7716 #7150 2021-12-07 21:58:11 +01:00
Koen Kanters d4f560d198 Validate QOS value. https://github.com/Koenkk/zigbee2mqtt/issues/9962 2021-12-07 18:52:54 +01:00
github-actions[bot]andKoenkk 4136b1e539 Update zigbee-herdsman to 0.13.179 (#10072)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-07 17:30:28 +00:00
github-actions[bot]andKoenkk b4af8329e7 Update zigbee-herdsman-converters to 14.0.341 (#10049)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-06 20:46:06 +00:00
github-actions[bot]andKoenkk ac0aca59a3 Update zigbee-herdsman-converters to 14.0.340 (#10046)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-06 19:40:28 +00:00
github-actions[bot]andnurikk bc8274b9fd Update zigbee2mqtt-frontend to 0.6.58 (#10035)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-06 03:48:12 +00:00
github-actions[bot]andKoenkk e5321d3f20 Update zigbee-herdsman-converters to 14.0.339 (#10021)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-05 14:49:20 +00:00
github-actions[bot]andnurikk 7a81a04b2f Update zigbee2mqtt-frontend to 0.6.57 (#10023)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-05 14:44:58 +00:00
github-actions[bot]andKoenkk 3b3d886121 Update zigbee-herdsman to 0.13.178 (#10022)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-05 14:30:05 +00:00
TilmanK 48b790cbf3 Adds link to web interface to the readme (#10015) 2021-12-05 15:05:26 +01:00
9c93103fb7 Update dependencies (#10011)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-12-05 15:01:53 +01:00
github-actions[bot]andnurikk 33b35c1e61 Update zigbee2mqtt-frontend to 0.6.56 (#10017)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-05 10:36:58 +00:00
github-actions[bot]andKoenkk 073c7aedf7 Update zigbee-herdsman-converters to 14.0.337 (#10000)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-04 15:20:48 +00:00
github-actions[bot]andKoenkk e789514777 Update zigbee-herdsman to 0.13.177 (#9999)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-04 14:31:30 +00:00
github-actions[bot]andnurikk fd762fcf10 Update zigbee2mqtt-frontend to 0.6.55 (#9998)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-04 13:26:16 +00:00
github-actions[bot]andnurikk 45b49bac0a Update zigbee2mqtt-frontend to 0.6.54 (#9997)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-04 12:30:48 +00:00
github-actions[bot]andnurikk 055fba259a Update zigbee2mqtt-frontend to 0.6.53 (#9996)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-04 09:49:59 +00:00
github-actions[bot]andnurikk b206dc910f Update zigbee2mqtt-frontend to 0.6.52 (#9995)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-04 09:40:34 +00:00
github-actions[bot]andnurikk 70db18f3ab Update zigbee2mqtt-frontend to 0.6.51 (#9992)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-04 03:33:58 +00:00
Koen Kanters cb2ef51df8 Fix last seen not always updated. #7423 2021-12-03 19:21:52 +01:00
Koen Kanters 68e5b39172 Mock update.state for Home Asisstant sensor. https://github.com/Koenkk/zigbee2mqtt/issues/9939 2021-12-03 19:08:00 +01:00
github-actions[bot]andnurikk b17ea2e4d9 Update zigbee2mqtt-frontend to 0.6.50 (#9982)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-03 16:17:11 +00:00
github-actions[bot]andnurikk 41f9361922 Update zigbee2mqtt-frontend to 0.6.49 (#9975)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-03 11:45:25 +00:00
github-actions[bot]andnurikk dae66fd3fc Update zigbee2mqtt-frontend to 0.6.48 (#9955)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-02 13:28:03 +00:00
github-actions[bot]andnurikk d6ea54b8df Update zigbee2mqtt-frontend to 0.6.47 (#9953)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-02 12:25:07 +00:00
Koen Kanters c47b9c7c9b Promote to dev 2021-12-01 17:21:30 +01:00
Koen Kanters c2b5229f76 1.22.1 2021-12-01 17:20:47 +01:00
Koen Kanters 0c605688af Undev 2021-12-01 17:20:43 +01:00
Koen Kanters 6876e19008 Merge branch 'dev' 2021-12-01 17:19:37 +01:00
github-actions[bot]andKoenkk 437b1dfb3e Update zigbee-herdsman-converters to 14.0.336 (#9930)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-12-01 16:15:02 +00:00
github-actions[bot]andnurikk fda1f23829 Update zigbee2mqtt-frontend to 0.6.46 (#9928)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-01 12:02:13 +00:00
ed9d09a629 Replace serve-static with connect-gzip-static (#9921)
* Replace serve-static with connect-gzip-static in order to serve .gz files

* Make linter happy

Co-authored-by: nurikk <ainur@yojee.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-12-01 07:20:53 +00:00
github-actions[bot]andnurikk d298412a1d Update zigbee2mqtt-frontend to 0.6.45 (#9922)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-12-01 02:20:56 +00:00
Koen Kanters e7447cf1e3 Another attempt to fix https://github.com/Koenkk/zigbee2mqtt/issues/9822 2021-11-30 20:20:46 +01:00
c90171d2cc Update zigbee-herdsman to 0.13.176 (#9916)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-11-30 18:26:53 +00:00
github-actions[bot]andKoenkk d2cf0dd8aa Update zigbee-herdsman-converters to 14.0.335 (#9915)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-30 18:22:06 +00:00
Hai-Nam Nguyen d415f57e30 Fix link to Zigbee cluster library (#9901) 2021-11-30 17:02:56 +00:00
github-actions[bot]andnurikk 56998f72f5 Update zigbee2mqtt-frontend to 0.6.44 (#9909)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-11-30 14:36:40 +00:00
github-actions[bot]andnurikk 97bd090ad6 Update zigbee2mqtt-frontend to 0.6.43 (#9908)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-11-30 14:10:51 +00:00
amadeo-alexandKoen Kanters df182be21f Fix log commit hash newline (#9897)
* Added trim operation before commitHash is returned

* Update utils.ts

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-11-30 12:24:36 +08:00
290a0312d4 Update zigbee-herdsman to 0.13.175 (#9894)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-11-29 17:46:28 +00:00
github-actions[bot]andKoenkk 4d432658e9 Update zigbee-herdsman-converters to 14.0.334 (#9893)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-29 17:20:10 +00:00
Koen Kanters c2d9ffa7ad Fix "Possible EventEmitter memory leak detected. 1001 drain listeners added to [Socket]" warning. #9822 2021-11-29 17:49:54 +01:00
github-actions[bot]andnurikk 82f5ddb748 Update zigbee2mqtt-frontend to 0.6.42 (#9882)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-11-29 01:29:37 +00:00
github-actions[bot]andKoenkk 1069ec2a1a Update zigbee-herdsman-converters to 14.0.333 (#9879)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-28 19:14:34 +00:00
Koen Kanters cd4ac0c174 Ignore messages from coordinator. https://github.com/Koenkk/zigbee2mqtt/issues/9218 2021-11-28 20:08:09 +01:00
134b80ccf6 Update dependencies (#9870)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-11-28 17:34:57 +00:00
github-actions[bot]andnurikk b07450d413 Update zigbee2mqtt-frontend to 0.6.41 (#9875)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-11-28 15:19:05 +00:00
Koen Kanters 65f7fc0532 Fix lint errors of 0a7a477281 2021-11-27 09:36:03 +01:00
Koen Kanters 0a7a477281 Send only 1 MQTT message when last_seen is enabled and Zigbee message is received. https://github.com/Koenkk/zigbee2mqtt/issues/9519 2021-11-27 09:31:31 +01:00
github-actions[bot]andKoenkk a170f304c7 Update zigbee-herdsman to 0.13.174 (#9862)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-27 08:30:42 +00:00
github-actions[bot]andKoenkk 35ed5154c8 Update zigbee-herdsman-converters to 14.0.332 (#9861)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-27 07:42:00 +00:00
Koen Kanters 641853e6d5 Revert "Increase max event listeners. https://github.com/Koenkk/zigbee2mqtt/issues/9822"
This reverts commit 51321affcf.
2021-11-27 08:38:33 +01:00
github-actions[bot]andKoenkk 2af9f21961 Update zigbee-herdsman-converters to 14.0.331 (#9859)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-26 21:19:41 +00:00
Koen Kanters 51321affcf Increase max event listeners. https://github.com/Koenkk/zigbee2mqtt/issues/9822 2021-11-25 21:49:50 +01:00
github-actions[bot]andKoenkk 5fca6fbd09 Update zigbee-herdsman-converters to 14.0.330 (#9842)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-25 15:55:42 +00:00
github-actions[bot]andnurikk 070065a248 Update zigbee2mqtt-frontend to 0.6.40 (#9841)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-11-25 14:01:40 +00:00
github-actions[bot]andKoenkk 9a8f204683 Update zigbee-herdsman-converters to 14.0.329 (#9831)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-24 20:47:44 +00:00
github-actions[bot]andKoenkk 167c05681e Update zigbee-herdsman to 0.13.173 (#9817)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-23 19:40:54 +00:00
github-actions[bot]andKoenkk 01d7bd25be Update zigbee-herdsman-converters to 14.0.328 (#9816)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-23 19:20:32 +00:00
tteckster 372ec315b5 Update settings.schema.json (#9803)
* Update settings.schema.json

* Update settings.schema.json
2021-11-23 18:00:44 +00:00
Dmitriy Fishman df0543d4b7 Update README.md (#9801) 2021-11-23 18:00:16 +00:00
github-actions[bot]andKoenkk a9e3fb3358 Update zigbee-herdsman-converters to 14.0.327 (#9799)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-22 21:34:30 +00:00
github-actions[bot]andKoenkk 678e19fd7e Update zigbee-herdsman-converters to 14.0.326 (#9795)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-22 19:05:41 +00:00
github-actions[bot]andnurikk 9128838534 Update zigbee2mqtt-frontend to 0.6.39 (#9789)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-11-22 14:03:18 +00:00
github-actions[bot]andnurikk 35876616ef Update zigbee2mqtt-frontend to 0.6.37 (#9786)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-11-22 07:10:51 +00:00
github-actions[bot]andKoenkk 0bc3129b7a Update zigbee-herdsman-converters to 14.0.325 (#9781)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-21 21:30:33 +00:00
Koen Kanters 5e3b18a053 Log failed to ping as warning instead of error. https://github.com/Koenkk/zigbee2mqtt/issues/9700 2021-11-21 22:11:10 +01:00
github-actions[bot]andnurikk ad3ff7fa40 Update zigbee2mqtt-frontend to 0.6.36 (#9772)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-11-21 04:15:04 +00:00
github-actions[bot]andnurikk 008d6f9aa0 Update zigbee2mqtt-frontend to 0.6.35 (#9771)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-11-21 04:03:44 +00:00
github-actions[bot]andKoenkk 854353fb71 Update dependencies (#9770)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-21 01:15:28 +01:00
github-actions[bot]andnurikk e35ba0f83c Update zigbee2mqtt-frontend to 0.6.34 (#9764)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-11-20 16:58:33 +00:00
github-actions[bot]andKoenkk a154e18326 Update zigbee-herdsman-converters to 14.0.324 (#9761)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-20 13:12:15 +00:00
Filip SandborgandKoen Kanters c793cc83ad Expose pi_heating_demand to Home Assistant (#9759)
* homeassistant: expose pi_heating_demand on climate

This allows homeassistant to access the actual percentage actuation currently used on thermostats.

* Update homeassistant.ts

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-11-20 13:59:04 +01:00
Koen Kanters a51626e357 Add sleep before enable-pull-request-automerge 2021-11-19 19:51:14 +01:00
github-actions[bot]andKoenkk 0f3315063f Update zigbee-herdsman-converters to 14.0.323 (#9751)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-19 18:47:34 +00:00
Koen Kanters 1065916a01 Enable auto merge for update_frontend 2021-11-19 09:36:17 +01:00
github-actions[bot]andKoenkk a980b4dff5 Update zigbee-herdsman to 0.13.172 (#9731)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-18 20:08:18 +00:00
Koen Kanters 825c7465ae Merge branch 'master' into dev 2021-11-18 20:59:53 +01:00
Koen Kanters 97521478c6 Enable auto merge for update_zhc/zh 2021-11-18 20:57:29 +01:00
Koen Kanters 6279c83e65 Merge branch 'master' into dev 2021-11-18 20:14:37 +01:00
Koen Kanters 91479fc0ec Enable auto merge for update_zhc/zh 2021-11-18 20:14:26 +01:00
github-actions[bot]andKoenkk 9dbe6f0a6c Update zigbee-herdsman-converters to 14.0.322 (#9725)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-18 19:10:01 +00:00
Koen Kanters 5d415aa299 Support multiple endpoints for Home Assistant tilt cover. https://github.com/Koenkk/zigbee-herdsman-converters/issues/3376 2021-11-18 19:43:38 +01:00
github-actions[bot]andKoenkk 6a75e0d4ee Update zigbee-herdsman-converters to 14.0.321 (#9720)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-17 22:08:15 +01:00
github-actions[bot]andKoenkk 13e9616c92 Update zigbee-herdsman-converters to 14.0.320 (#9716)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-17 19:56:28 +01:00
Koen Kanters cc03be46df Fix properties missing when debounce is used under some conditions #9658 (#9682)
* Add logging

* Kill bugs

* Updates
2021-11-17 17:59:00 +00:00
github-actions[bot]andKoenkk 64154e0cb2 Update zigbee-herdsman-converters to 14.0.319 (#9696)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-16 19:42:18 +01:00
github-actions[bot]andnurikk b2eaa3141e Update zigbee2mqtt-frontend to 0.6.33 (#9663)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-11-15 16:36:31 +08:00
github-actions[bot]andKoenkk d192bbd28f Update zigbee-herdsman-converters to 14.0.318 (#9654)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-14 16:20:45 +00:00
Koen Kanters 9582808b09 Prepare for https://github.com/Koenkk/zigbee2mqtt/pull/9654 2021-11-14 17:18:01 +01:00
github-actions[bot]andKoenkk 623f999ec8 Update zigbee-herdsman to 0.13.170 (#9655)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-14 16:43:51 +01:00
Koen Kanters 3141a14fec Attempt to fix race condition in availability stop. https://github.com/Koenkk/zigbee2mqtt/issues/9644 2021-11-14 16:18:54 +01:00
Koen Kanters 7e2a2bdd34 Remove led control (#9404) 2021-11-14 15:04:23 +00:00
github-actions[bot]andKoenkk 2f503bf39a Update dependencies (#9647)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-14 16:02:25 +01:00
Ernst 945b72b990 a few simple language fixes (README.md) (#9642) 2021-11-13 16:09:46 +00:00
github-actions[bot]andKoenkk 3bfc7f31cf Update zigbee-herdsman-converters to 14.0.317 (#9632)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-13 10:13:28 +01:00
github-actions[bot]andnurikk 1f60edff99 Update zigbee2mqtt-frontend to 0.6.32 (#9628)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-11-13 13:31:39 +08:00
github-actions[bot]andKoenkk 3249ab00ab Update zigbee-herdsman-converters to 14.0.316 (#9624)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-12 18:38:26 +01:00
github-actions[bot]andKoenkk 8c8f907fd3 Update zigbee-herdsman to 0.13.169 (#9606)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-11 21:15:38 +01:00
Koen Kanters 170ef40982 Fix startup blocked by onEvent handlers 2021-11-11 21:00:16 +01:00
github-actions[bot]andKoenkk e547062702 Update zigbee-herdsman-converters to 14.0.315 (#9605)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-11 20:47:54 +01:00
Artem DraftandKoen Kanters 6ca8d35183 Expose local_temperature_calibration to Home Assistant (#9595)
* expose local_temperature_calibration in HA as numeric

* fix eslint

* added min/max logic

* remove magic values

* Update homeassistant.ts

* Update types.d.ts

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-11-10 20:17:56 +00:00
Koen Kanters 420bae4259 Fix Home Assistant command topics for multi endpoint climate devices. https://github.com/Koenkk/zigbee-herdsman-converters/pull/3217 2021-11-10 21:07:47 +01:00
github-actions[bot]andKoenkk 97dacd7623 Update zigbee-herdsman-converters to 14.0.314 (#9596)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-10 20:45:28 +01:00
Kenny LevinsenandKoen Kanters 8ef82fe23f receive: Add genPollCtrl to ignored clusters (#9580)
zigbee-herdsman may bind genPollCtrl for poll control integration, but
that does not mean that there will be a converter available further up
the stack.

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-11-10 18:44:07 +00:00
github-actions[bot]andKoenkk cd3df10a54 Update zigbee-herdsman-converters to 14.0.313 (#9563)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-08 19:03:42 +00:00
github-actions[bot]andKoenkk d5ccd9221c Update zigbee-herdsman to 0.13.168 (#9562)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-08 18:49:16 +00:00
github-actions[bot]andKoenkk fc9abfe53b Update zigbee-herdsman-converters to 14.0.311 (#9552)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-07 20:58:55 +01:00
01ea46b280 Update zigbee-herdsman-converters to 14.0.310 (#9549)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-11-07 18:40:12 +00:00
github-actions[bot]andKoenkk ab629d52ea Update zigbee-herdsman to 0.13.167 (#9548)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-07 19:37:44 +01:00
Adam BelebczukandKoen Kanters 9e216011d4 Fix Home Assistant discovery message for running_state fan_only (#9547)
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-11-07 17:38:51 +00:00
Koen Kanters 91d90bdcf1 Merge remote-tracking branch 'origin/master' into dev 2021-11-07 18:37:01 +01:00
github-actions[bot]andKoenkk 6ba743cbed Update dependencies (#9538)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-07 12:22:36 +01:00
2399ec040f Update zigbee-herdsman-converters to 14.0.309 (#9526)
* Update zigbee-herdsman-converters to 14.0.309

* Update receive.test.js

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-11-05 19:41:23 +00:00
Koen Kanters f151e1d371 Fix 89af770d96 for real now 2021-11-05 20:39:12 +01:00
Koen Kanters 55802ef648 Fix 89af770d96 2021-11-05 20:31:09 +01:00
Koen Kanters 89af770d96 Fix external extensions not started. https://github.com/Koenkk/zigbee2mqtt/issues/9520 2021-11-05 20:24:09 +01:00
Koen Kanters 0efd0cfe17 Fix min/max value for Home Assistant number entity. https://github.com/Koenkk/zigbee2mqtt/issues/9012 2021-11-05 19:30:14 +01:00
Franck Nijhof c852434dde Cleanup unused legacyApi variable (#9512) 2021-11-05 19:20:42 +01:00
Franck Nijhof 57e93a48ff Fix HA discovery device firmware version (#9511) 2021-11-05 19:19:55 +01:00
Pavel Sokolov 181944d2ff Fix invalid frontend.host default value in schema (#9508) 2021-11-05 19:08:34 +01:00
Franck NijhofandKoen Kanters a09aa4243e Improve expose of update sensors to HA (#9306)
* Improve expose of update sensors to HA

* Update homeassistant.ts

* Update homeassistant.ts

* Update homeassistant.test.js

* Update homeassistant.test.js

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-11-04 21:11:58 +01:00
932ee433c4 Update zigbee-herdsman-converters to 14.0.308 (#9500)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-11-04 20:53:06 +01:00
Koen Kanters 20b6fd42d3 Stop pinging device (availability feature) when it is removed. https://github.com/Koenkk/zigbee2mqtt/issues/9490 2021-11-04 20:52:10 +01:00
github-actions[bot]andKoenkk 178da2db1d Update zigbee-herdsman to 0.13.166 (#9501)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-04 20:47:50 +01:00
github-actions[bot]andnurikk e504da33d4 Update zigbee2mqtt-frontend to 0.6.31 (#9498)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-11-05 00:12:30 +08:00
github-actions[bot]andKoenkk fa1775d8b4 Update zigbee-herdsman-converters to 14.0.307 (#9488)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-03 21:05:21 +01:00
Franck NijhofandKoen Kanters 2612de3c34 Correct device class and entity category for last seen HA sensor (#9305)
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-11-03 20:36:01 +01:00
Franck NijhofandKoen Kanters 416f4fa548 Mark indicator led HA switch as entity type config (#9309)
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-11-03 20:31:31 +01:00
Franck Nijhof ee57b0ce0b Add support for unit of measurement to HA number entities (#9312) 2021-11-03 20:24:55 +01:00
Franck Nijhof 9729a72d33 Add Home Assistant entity_category support (#9293) 2021-11-03 20:23:46 +01:00
github-actions[bot]andKoenkk 23719df7ac Update zigbee-herdsman-converters to 14.0.306 (#9485)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-03 20:10:35 +01:00
github-actions[bot]andKoenkk ef6bf0a8f9 Update zigbee-herdsman-converters to 14.0.305 (#9466)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-02 18:29:04 +01:00
ciB89 b3e9afaf3e fix link (#9462)
fix one link not working (still directing to old site)
2021-11-02 17:50:25 +01:00
github-actions[bot]andKoenkk 5709898c94 Update zigbee-herdsman-converters to 14.0.304 (#9448)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-01 20:53:15 +01:00
Koen Kanters 68b5be0ce7 Properly handle update available check failing for IKEA device. https://github.com/Koenkk/zigbee-herdsman-converters/issues/3253 2021-11-01 19:09:34 +01:00
Koen Kanters 62bf76c11f Fix site links 2021-11-01 18:12:27 +01:00
Koen Kanters 243d333526 Merge branch 'dev' of github.com:Koenkk/zigbee2mqtt into dev 2021-11-01 18:12:15 +01:00
09383877b0 Update dependencies (#9444)
* Update dependencies

* Fix lint

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-11-01 18:10:54 +01:00
Koen Kanters 359af6f9ff Merge remote-tracking branch 'origin/master' into dev 2021-11-01 18:10:00 +01:00
Koen Kanters cf68507ec3 Update site links 2021-11-01 18:09:42 +01:00
github-actions[bot]andKoenkk 78a3e4a371 Update zigbee-herdsman to 0.13.165 (#9446)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-01 18:08:41 +01:00
Alexey KotenkoandKoen Kanters 11753a7078 Update new_device_support.md (#9436)
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-11-01 17:59:35 +01:00
Koen Kanters 512b785427 Promote to dev 2021-11-01 17:32:01 +01:00
Koen Kanters 0a1fccf6b3 1.22.0 2021-11-01 17:30:27 +01:00
Koen Kanters 9e4b65b188 Undev 2021-11-01 17:30:23 +01:00
github-actions[bot]andKoenkk 39c0b522d9 Update zigbee-herdsman-converters to 14.0.303 (#9443)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-11-01 17:28:04 +01:00
github-actions[bot]andnurikk 109c62bb4d Update zigbee2mqtt-frontend to 0.6.30 (#9438)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-11-01 19:42:05 +08:00
github-actions[bot]andnurikk a2e3f60de3 Update zigbee2mqtt-frontend to 0.6.28 (#9425)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-10-31 22:45:37 +08:00
github-actions[bot]andKoenkk bbf05b5c95 Update zigbee-herdsman-converters to 14.0.302 (#9418)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-30 22:55:00 +02:00
github-actions[bot]andnurikk a5413b9254 Update zigbee2mqtt-frontend to 0.6.26 (#9417)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-10-31 01:23:54 +08:00
github-actions[bot]andKoenkk d6890b5fe4 Update zigbee-herdsman-converters to 14.0.301 (#9407)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-30 10:24:04 +02:00
github-actions[bot]andKoenkk b1c1637547 Update zigbee-herdsman-converters to 14.0.298 (#9401)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-29 17:14:45 +02:00
Koen Kanters ed8ca293a3 Log stack in debug when MQTT request fails. https://github.com/Koenkk/zigbee2mqtt/issues/9367 2021-10-29 17:10:18 +02:00
github-actions[bot]andKoenkk abd4b23024 Update zigbee-herdsman-converters to 14.0.297 (#9387)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-28 17:50:38 +02:00
github-actions[bot]andKoenkk 7a198ce4a3 Update zigbee-herdsman to 0.13.164 (#9385)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-28 17:32:35 +02:00
github-actions[bot]andKoenkk e0c3ea28c0 Update zigbee-herdsman-converters to 14.0.296 (#9370)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-27 21:26:28 +02:00
Koen Kanters d137d1975b Fix value_min/max not correct for Home Assistant number when 0. https://github.com/Koenkk/zigbee-herdsman-converters/issues/3229 2021-10-27 20:52:54 +02:00
github-actions[bot]andKoenkk e7bc8b9a9a Update zigbee-herdsman-converters to 14.0.295 (#9351)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-26 21:07:57 +02:00
github-actions[bot]andKoenkk 72ffe4d118 Update zigbee-herdsman-converters to 14.0.294 (#9348)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-26 20:43:40 +02:00
github-actions[bot]andKoenkk e81d197979 Update zigbee-herdsman to 0.13.163 (#9342)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-26 17:03:57 +02:00
github-actions[bot]andnurikk 1dc36f4cc1 Update zigbee2mqtt-frontend to 0.6.25 (#9340)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-10-26 19:43:23 +08:00
github-actions[bot]andnurikk 252994968d Update zigbee2mqtt-frontend to 0.6.24 (#9321)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-10-26 10:05:20 +08:00
7fed9e7334 Update zigbee-herdsman-converters to 14.0.293 (#9330)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-10-25 18:08:32 +02:00
Koen Kanters bef5bff637 Revert "Add Home Assistant entity_category support (#9293)" (#9328)
This reverts commit 16c3f912b1.
2021-10-25 17:58:42 +02:00
Koen Kanters 498a5b3f32 Revert "Add support for unit of measurement to HA number entities (#9312)" (#9329)
This reverts commit b08ce534c8.
2021-10-25 17:54:18 +02:00
Franck Nijhof b08ce534c8 Add support for unit of measurement to HA number entities (#9312) 2021-10-25 17:12:01 +02:00
56812e7844 Update zigbee-herdsman-converters to 14.0.292 (#9303)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-10-24 15:57:04 +02:00
github-actions[bot]andKoenkk d19c60be07 Update zigbee-herdsman to 0.13.162 (#9302)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-24 15:43:11 +02:00
Dirk van DonkelaarandKoen Kanters 387378e935 Fix Home Assistant discovery for select having numbers as options (#9268)
* Updated select entities to parse values as string

To prevent 'invalid option for select' error

* Update homeassistant.ts

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-10-24 15:22:09 +02:00
Franck Nijhof 16c3f912b1 Add Home Assistant entity_category support (#9293) 2021-10-24 15:11:56 +02:00
github-actions[bot]andKoenkk 3f0a2fed9d Update zigbee-herdsman-converters to 14.0.291 (#9292)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-23 14:15:47 +02:00
github-actions[bot]andnurikk c8e3c9b5a2 Update zigbee2mqtt-frontend to 0.6.22 (#9290)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-10-23 17:12:47 +08:00
Koen Kanters b665106d44 Expose converter options (#9110)
* Expose converter options

* Updates
2021-10-23 10:27:16 +02:00
github-actions[bot]andKoenkk 88314f1c6a Update zigbee-herdsman-converters to 14.0.290 (#9289)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-23 10:24:40 +02:00
github-actions[bot]andnurikk 8c12551ac8 Update zigbee2mqtt-frontend to 0.6.21 (#9288)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-10-23 12:40:46 +08:00
github-actions[bot]andKoenkk 011d02d0e6 Update zigbee-herdsman-converters to 14.0.289 (#9279)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-22 19:47:12 +02:00
github-actions[bot]andKoenkk 8ac3989ada Update zigbee-herdsman-converters to 14.0.288 (#9263)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-21 18:18:42 +02:00
github-actions[bot]andKoenkk caa22c559e Update zigbee-herdsman to 0.13.161 (#9261)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-21 18:02:52 +02:00
github-actions[bot]andKoenkk 7469e29877 Update zigbee-herdsman-converters to 14.0.287 (#9255)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-20 19:28:39 +02:00
github-actions[bot]andKoenkk c647e87641 Update zigbee-herdsman-converters to 14.0.286 (#9238)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-19 20:01:18 +02:00
github-actions[bot]andKoenkk de825014c0 Update zigbee-herdsman-converters to 14.0.285 (#9230)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-19 17:24:45 +02:00
Koen Kanters ca7bb0cd4d Attempt to fix Home Assistant error when running_state is unknown. https://github.com/Koenkk/zigbee2mqtt/issues/9219 2021-10-19 16:56:31 +02:00
github-actions[bot]andKoenkk 158341a9ff Update zigbee-herdsman to 0.13.160 (#9226)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-19 16:43:17 +02:00
Koen Kanters eaad39a199 Expose device and group configuration_url to Home Assistant. (#9214) 2021-10-18 18:16:00 +02:00
github-actions[bot]andKoenkk ca255d3aa3 Update zigbee-herdsman-converters to 14.0.283 (#9213)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-18 17:49:13 +02:00
Koen Kanters 97d15b8d15 Home Assistant fix discovery for covers only supporting tilt. https://github.com/Koenkk/zigbee-herdsman-converters/issues/3122 2021-10-18 17:34:40 +02:00
Koen Kanters 549ccf036e Fix start being called twice for external extensions. https://github.com/Koenkk/zigbee2mqtt/issues/9165 2021-10-17 18:19:13 +02:00
github-actions[bot]andKoenkk 8ae4909836 Update zigbee-herdsman-converters to 14.0.282 (#9193)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-17 13:53:54 +02:00
github-actions[bot]andKoenkk bfb2427a6c Update zigbee-herdsman-converters to 14.0.281 (#9168)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-16 10:08:03 +02:00
github-actions[bot]andKoenkk 3571937060 Update zigbee-herdsman-converters to 14.0.280 (#9163)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-15 17:31:12 +02:00
Koen Kanters f7c0b46bef Catch exception when removing device via pass/blocklist. https://github.com/Koenkk/zigbee2mqtt/issues/9116 2021-10-14 22:18:37 +02:00
github-actions[bot]andKoenkk ad5cda4d40 Update zigbee-herdsman to 0.13.159 (#9156)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-14 22:12:08 +02:00
github-actions[bot]andKoenkk d18455b596 Update zigbee-herdsman-converters to 14.0.279 (#9153)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-14 20:31:51 +02:00
github-actions[bot]andKoenkk 0ae7bd0a2d Update zigbee-herdsman-converters to 14.0.278 (#9143)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-13 22:09:01 +02:00
Koen Kanters 8fac172ced Fix crash when event listener throws exception. https://github.com/Koenkk/zigbee2mqtt/commit/749381bf2f6fdb8788d5f5f7857ae75e5f73303a 2021-10-13 21:57:04 +02:00
github-actions[bot]andKoenkk cff4ea726e Update zigbee-herdsman-converters to 14.0.277 (#9141)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-13 19:09:31 +02:00
Koen Kanters 749381bf2f Fix crash when converter throws exception. https://github.com/Koenkk/zigbee2mqtt/issues/9133 2021-10-13 19:05:14 +02:00
Koen Kanters 6b380526ae Fix no configure on device interview. https://github.com/Koenkk/zigbee2mqtt/issues/8971 2021-10-13 18:32:19 +02:00
github-actions[bot]andKoenkk e8dd29e183 Update zigbee-herdsman to 0.13.158 (#9140)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-13 17:32:46 +02:00
github-actions[bot]andKoenkk b58e123594 Update zigbee-herdsman-converters to 14.0.276 (#9135)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-12 19:51:49 +02:00
github-actions[bot]andnurikk 899383d79e Update zigbee2mqtt-frontend to 0.6.20 (#9129)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-10-12 18:45:58 +08:00
github-actions[bot]andKoenkk de7b120ed2 Update zigbee-herdsman-converters to 14.0.275 (#9123)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-11 17:31:29 +02:00
Koen Kanters 4608de5d57 Allow publishing to device by endpoint ID. https://github.com/nurikk/zigbee2mqtt-frontend/issues/926 2021-10-11 17:30:48 +02:00
github-actions[bot]andnurikk a362e6c99d Update zigbee2mqtt-frontend to 0.6.19 (#9122)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-10-11 23:27:54 +08:00
github-actions[bot]andnurikk ed8a863c30 Update zigbee2mqtt-frontend to 0.6.18 (#9112)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-10-11 10:10:57 +08:00
github-actions[bot]andKoenkk 7086bc3e9d Update zigbee-herdsman-converters to 14.0.274 (#9111)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-10 21:08:18 +02:00
github-actions[bot]andKoenkk 2144e9b622 Update zigbee-herdsman-converters to 14.0.273 (#9109)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-10 20:55:14 +02:00
github-actions[bot]andnurikk ff5411a328 Update zigbee2mqtt-frontend to 0.6.17 (#9100)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-10-10 15:26:15 +08:00
github-actions[bot]andKoenkk 47297983c2 Update zigbee-herdsman-converters to 14.0.272 (#9094)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-09 20:42:49 +02:00
Koen Kanters a774b43d63 Fix Home Assistant template variable warnings (#9088)
* Fix more template warnings. https://github.com/Koenkk/zigbee2mqtt/issues/8769

* Update

* Updates
2021-10-09 19:41:00 +02:00
Koen Kanters 7f8632437d Add polyfills. https://github.com/Koenkk/zigbee2mqtt/issues/9085 2021-10-09 17:18:31 +02:00
Koen Kanters 6977235200 Revert "Fix group publish on Node 10 (doesn't support Object.fromEntries). https://github.com/Koenkk/zigbee2mqtt/issues/9085"
This reverts commit 4e520bb042.
2021-10-09 17:01:26 +02:00
Koen Kanters 4e520bb042 Fix group publish on Node 10 (doesn't support Object.fromEntries). https://github.com/Koenkk/zigbee2mqtt/issues/9085 2021-10-09 15:50:03 +02:00
45f096ec0a Update zigbee-herdsman to 0.13.157 (#9087)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-10-09 10:45:18 +02:00
github-actions[bot]andKoenkk 0ea74e18b2 Update zigbee-herdsman-converters to 14.0.271 (#9086)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-09 10:44:37 +02:00
Koen Kanters ab5d368a4e Small Home Assistant cleanup 2021-10-09 10:42:52 +02:00
Koen Kanters ebd20e30dc Home Assistant: discover last_seen sensor when enabled. https://github.com/Koenkk/zigbee2mqtt/issues/8287 2021-10-09 10:19:07 +02:00
github-actions[bot]andKoenkk c2d4335e45 Update zigbee-herdsman to 0.13.156 (#9080)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-08 17:10:12 +02:00
38cbfa4e91 Update zigbee-herdsman to 0.13.155 (#9079)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2021-10-08 16:51:21 +02:00
github-actions[bot]andKoenkk f7fd0ce825 Update zigbee-herdsman-converters to 14.0.270 (#9078)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-08 16:47:57 +02:00
github-actions[bot]andnurikk ea550a8fcf Update zigbee2mqtt-frontend to 0.6.16 (#9076)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-10-08 21:14:09 +08:00
github-actions[bot]andnurikk 9907e59fa7 Update zigbee2mqtt-frontend to 0.6.15 (#9070)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2021-10-08 19:47:31 +08:00
Koen Kanters 3b0a906a97 Expose scenes (#9056)
* Expose scenes

* Republish devices and groups when changed.

* Send scene name if set.
2021-10-08 19:02:24 +08:00
github-actions[bot]andKoenkk c88f643407 Update zigbee-herdsman-converters to 14.0.269 (#9074)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-08 12:35:17 +02:00
github-actions[bot]andKoenkk 89d2b0088d Update zigbee-herdsman-converters to 14.0.268 (#9062)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-07 22:11:45 +02:00
github-actions[bot]andKoenkk d03c5e2372 Update zigbee-herdsman to 0.13.154 (#9060)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-07 21:07:43 +02:00
github-actions[bot]andKoenkk 90d20f936e Update zigbee-herdsman-converters to 14.0.267 (#9059)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-07 20:37:26 +02:00
Koen Kanters 6f9371e4b0 Fix incorrect state when last_seen and debounce is enabled. https://github.com/Koenkk/zigbee2mqtt/issues/8958 https://github.com/Koenkk/zigbee2mqtt/issues/9010 2021-10-07 20:36:42 +02:00
github-actions[bot]andKoenkk 4082763f59 Update zigbee-herdsman to 0.13.153 (#9055)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-07 18:19:07 +02:00
Koen Kanters 42497f3176 Fix some todos 2021-10-06 21:27:23 +02:00
github-actions[bot]andKoenkk 7ea29eadf4 Update zigbee-herdsman-converters to 14.0.266 (#9041)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-06 20:58:55 +02:00
github-actions[bot]andKoenkk db8d0ecf20 Update zigbee-herdsman-converters to 14.0.265 (#9026)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-05 22:20:17 +02:00
github-actions[bot]andKoenkk bd216d7a24 Update zigbee-herdsman to 0.13.152 (#9022)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-05 18:44:27 +02:00
Koen Kanters 23b87755cf Stop Zigbee2MQTT with error when initial MQTT connect fails. https://github.com/Koenkk/zigbee2mqtt/issues/8956 2021-10-04 22:08:06 +02:00
Koen Kanters 7415e8416d Update availability config title. https://github.com/Koenkk/zigbee2mqtt/issues/6281 2021-10-04 21:24:31 +02:00
Koen Kanters 8e2c79f85d Fix external converters not loaded. https://github.com/Koenkk/zigbee2mqtt/issues/8959 2021-10-04 21:22:44 +02:00
Koen Kanters ab3a9da630 Revert "Improve MQTT error handling. https://github.com/Koenkk/zigbee2mqtt/issues/8956"
This reverts commit 45a4978ca8.
2021-10-04 20:51:09 +02:00
github-actions[bot]andKoenkk 7ccf781b65 Update zigbee-herdsman-converters to 14.0.264 (#9003)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-04 17:58:41 +02:00
Koen Kanters 30badb62e7 Update availabiltiy config schema. https://github.com/Koenkk/zigbee2mqtt/issues/6281 2021-10-04 17:48:05 +02:00
github-actions[bot]andKoenkk a0512a489d Update zigbee-herdsman-converters to 14.0.263 (#8989)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-03 22:23:48 +02:00
github-actions[bot]andKoenkk edb145a8c3 Update zigbee-herdsman to 0.13.151 (#8972)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-03 21:46:31 +02:00
Koen Kanters 45a4978ca8 Improve MQTT error handling. https://github.com/Koenkk/zigbee2mqtt/issues/8956 2021-10-03 12:29:54 +02:00
github-actions[bot]andKoenkk fb96551beb Update dependencies (#8965)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-03 12:09:35 +02:00
github-actions[bot]andKoenkk 2fb59f8efa Update zigbee-herdsman-converters to 14.0.262 (#8951)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2021-10-02 11:04:22 +02:00
Koen Kanters c28731957a Fix last seen not published when changed. https://github.com/Koenkk/zigbee2mqtt/issues/7423 2021-10-02 10:49:47 +02:00
Koen Kanters 0282011255 Replace old availability implementation with new one (#8904)
* Update

* Updates

* More refactoringzzz

* Bindoo

* Loadz of typescripting

* Logga

* Updates

* Updates

* Updates

* Updates

* cleanup

* updates

* Fix coverage

* Fixes

* Updates

* Updates

* Replace old availability implementation with new one.
2021-10-02 10:25:43 +02:00
Koen Kanters 045ee573a0 Final TypeScript refactor (#8859)
* Update

* Updates

* More refactoringzzz

* Bindoo

* Loadz of typescripting

* Logga

* Updates

* Updates

* Updates

* Updates

* cleanup

* updates

* Fix coverage

* Fixes

* Updates

* Updates
2021-10-02 10:09:38 +02:00
Koen Kanters 839e280159 Dev 2021-10-01 17:54:04 +02:00
115 changed files with 23653 additions and 28881 deletions
+1
View File
@@ -15,6 +15,7 @@ module.exports = {
'indent': ['error', 4],
'max-len': ['error', {'code': 120}],
'no-prototype-builtins': 'off',
'linebreak-style': ['error', (process.platform === 'win32' ? 'windows' : 'unix')], // https://stackoverflow.com/q/39114446/2771889
},
'plugins': [
'jest',
-46
View File
@@ -1,46 +0,0 @@
<!--
Before submitting an issue make sure you've searched for a similar issue and read the documentation: https://www.zigbee2mqtt.io/.
Rules (don't ignore these, your issue will be closed without further notice):
- English only
- Make sure you are on the latest Zigbee2MQTT version
- Provide a clear description of the problem
- DON'T copy logs directly here, post a link to https://hastebin.com/ or https://pastebin.com/.
- Make sure you are running the latest firmware https://github.com/koenkk/z-stack-firmware.
Did you read the FAQ?
- https://www.zigbee2mqtt.io/information/FAQ.html
Zigbee2MQTT fails to start?
- https://www.zigbee2mqtt.io/information/FAQ.html#help-zigbee2mqtt-fails-to-start
Having issues when using a CC2531?
- Make sure the CC2531 is connected through a USB extension cable
- Try the source routing firmware: https://github.com/Koenkk/Z-Stack-firmware/tree/master/coordinator/Z-Stack_Home_1.2/bin/source_routing
- With larger networks (30/40+) devices, your CC2531 might not be powerful enough. This will cause weird issues, in this case it's advised to use a more powerful adapter: https://www.zigbee2mqtt.io/information/supported_adapters.html#texas-instruments-cc26x2r1
Unsupported device?
- https://www.zigbee2mqtt.io/how_tos/how_to_support_new_devices.html
Device does not pair or interview fails?
- https://www.zigbee2mqtt.io/information/FAQ.html#why-does-my-device-not-or-fail-to-pair
Bug report?
- If applicable, provide steps how to reproduce the problem.
- Provide the herdsman debug logging: https://www.zigbee2mqtt.io/information/debug.html#zigbee-herdsman-debug-logging
-->
<!--Start your bug report below this line-->
# Bug Report
## What happened
## What did you expect to happen
## How to reproduce it (minimal and precise)
## Debug Info
Zigbee2MQTT version:
Adapter hardware: CC2531, CC2530, CC26X2R1 or CC1352P-2
Adapter firmware version:
+4 -1
View File
@@ -1,5 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: "IMPORTANT: Check development branch changelog first!!"
url: https://github.com/Koenkk/zigbee2mqtt/releases/tag/latest-dev
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"
- name: Questions/discussion
url: https://github.com/Koenkk/zigbee2mqtt/discussions/new
about: Ask questions, discuss about devices or show things you made
@@ -10,7 +13,7 @@ contact_links:
url: https://github.com/zigbee2mqtt/hassio-zigbee2mqtt/issues
about: Issues related to the Home Assistant addon.
- name: FAQ
url: https://www.zigbee2mqtt.io/information/FAQ.html
url: https://www.zigbee2mqtt.io/guide/faq
about: Frequently asked questions.
- name: Support Chat
url: https://discord.gg/NyseBeK
-20
View File
@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: feature request
assignees: ''
---
## Is your feature request related to a problem? Please describe
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
## Describe the solution you'd like
A clear and concise description of what you want to happen.
## Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.
## Additional context
Add any other context or screenshots about the feature request here.
@@ -0,0 +1,38 @@
name: Feature request
description: Suggest an idea for this project
title: "[Feature request]: "
labels: [feature request]
body:
- type: markdown
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=)
- 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 [...]
validations:
required: true
- type: textarea
id: textarea2
attributes:
label: Describe the solution you'd like
placeholder: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
id: textarea3
attributes:
label: Describe alternatives you've considered
placeholder: A clear and concise description of any alternative solutions or features you've considered.
validations:
required: true
- type: textarea
id: textarea4
attributes:
label: Additional context
placeholder: Add any other context or screenshots about the feature request here.
validations:
required: true
@@ -1,21 +0,0 @@
---
name: New device support request
about: Request support for a new device
title: ''
labels: new device support
assignees: ''
---
<!--
VERY IMPORTANT: Before submitting a request, first (try to) follow: https://www.zigbee2mqtt.io/how_tos/how_to_support_new_devices.html
Dumping just the info about this device without actually attemping adding support yourself will likely lead into this issue being ignored.
Clearly indicate what you tried and where you got stuck.
-->
## Information about the device + link
..
## data/database.db entry of the device
..
@@ -0,0 +1,55 @@
name: New device support request
description: Request support for a new device
title: "[New device support]: "
labels: [new device support]
body:
- type: markdown
attributes:
value: |
**IMPORTANT:** Before submitting:
- Make sure this device is not already supported in the [dev branch](https://www.zigbee2mqtt.io/advanced/more/switch-to-dev-branch.html)
- Make sure there is no existing issue or PR for this device already, search for your device here: https://github.com/Koenkk/zigbee2mqtt/issues
- Follow this [guide](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html)
- 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: input
id: link
attributes:
label: Link
description: Link of this device (product page)
placeholder: https://www.linktomydevice.org
validations:
required: true
- type: input
id: database
attributes:
label: Database entry
description: Entry of this device in `data/database.db` after pairing it
placeholder: '{"id":53,"type":"Router","ieeeAddr":"0x10458d00024284f69","nwkAddr":10148,"manufId":4151,"manufName":"LUMI","powerSource":"DC Source","modelId":"lumi.relay.c2acn01","epList":[1,2],"endpoints":{"1":{"profId":260,"epId":1,"devId":257,"inClusterList":[0,3,4,5,1,2,10,6,16,2820,12],"outClusterList":[25,10],"clusters":{"genBasic":{"attributes":{"modelId":"lumi.relay.c2acn01","appVersion":1,"manufacturerName":"LUMI","powerSource":4,"zclVersion":0,"stackVersion":2,"hwVersion":18,"dateCode":"8-6-2020"}},"genAnalogInput":{"attributes":{"presentValue":129.04425048828125}},"genOnOff":{"attributes":{"61440":117440715,"onOff":1}}},"binds":[],"configuredReportings":[],"meta":{}},"2":{"profId":260,"epId":2,"devId":257,"inClusterList":[6,16,4,5],"outClusterList":[],"clusters":{"genOnOff":{"attributes":{"61440":237478966,"onOff":0}}},"binds":[],"configuredReportings":[],"meta":{}}},"appVersion":1,"stackVersion":2,"hwVersion":18,"dateCode":"8-6-2020","zclVersion":0,"interviewCompleted":true,"meta":{},"lastSeen":1640285631405}'
validations:
required: true
- type: textarea
id: notes
attributes:
label: Comments
placeholder: I tried to follow the supporting new device page but got stuck at...
validations:
required: true
- type: textarea
attributes:
label: External converter
description: In case you tried to create an external converter, put it below
render: shell
- type: markdown
attributes:
value: |
# Lights
In case this device is a light, fill in the fields below:
- type: input
attributes:
label: Supported color modes
placeholder: color temperature, color (rgb)
- type: input
attributes:
label: Color temperature range
description: In case this light supports color temperature, provide the color temperature range. [How to do this](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html#_3-1-retrieving-color-temperature-range-only-required-for-lights-which-support-color-temperature).
placeholder: '{"colorTempPhysicalMin":153,"colorTempPhysicalMax":454}'
-54
View File
@@ -1,54 +0,0 @@
---
name: Problem report
about: Create a report to help us improve
title: ''
labels: problem
assignees: ''
---
<!--
Before submitting an issue make sure you've searched for a similar issue and read the documentation: https://www.zigbee2mqtt.io/.
Rules (don't ignore these, your issue will be closed without further notice):
- English only
- Make sure you are on the latest Zigbee2MQTT version
- Provide a clear description of the problem
- DON'T copy logs directly here, post a link to https://hastebin.com/ or https://pastebin.com/.
- Make sure you are running the latest firmware https://github.com/koenkk/z-stack-firmware.
Did you read the FAQ?
- https://www.zigbee2mqtt.io/information/FAQ.html
Zigbee2MQTT fails to start?
- https://www.zigbee2mqtt.io/information/FAQ.html#help-zigbee2mqtt-fails-to-start
Having issues when using a CC2531?
- Make sure the CC2531 is connected through a USB extension cable
- Try the source routing firmware: https://github.com/Koenkk/Z-Stack-firmware/tree/master/coordinator/Z-Stack_Home_1.2/bin/source_routing
- With larger networks (30/40+) devices, your CC2531 might not be powerful enough. This will cause weird issues, in this case it's advised to use a more powerful adapter: https://www.zigbee2mqtt.io/information/supported_adapters.html#texas-instruments-cc26x2r1
Unsupported device?
- https://www.zigbee2mqtt.io/how_tos/how_to_support_new_devices.html
Device does not pair or interview fails?
- https://www.zigbee2mqtt.io/information/FAQ.html#why-does-my-device-not-or-fail-to-pair
Bug report?
- If applicable, provide steps how to reproduce the problem.
- Provide the herdsman debug logging: https://www.zigbee2mqtt.io/information/debug.html#zigbee-herdsman-debug-logging
-->
<!--Start your bug report below this line-->
## What happened
## What did you expect to happen
## How to reproduce it (minimal and precise)
## Debug info
Zigbee2MQTT version:
Adapter hardware: CC2531, CC2530, CC26X2R1, CC1352P-2, Conbee II
Adapter firmware version:
@@ -0,0 +1,67 @@
name: Problem report
description: Create a report to help us improve
labels: [problem]
body:
- type: markdown
attributes:
value: |
**IMPORTANT:** Before submitting:
- You read the [FAQ](https://www.zigbee2mqtt.io/guide/faq/)
- 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+)
- 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
attributes:
label: What happened?
validations:
required: true
- type: textarea
id: expect_to_happen
attributes:
label: What did you expect to happen?
placeholder: I expected that ...
validations:
required: false
- type: textarea
id: reproduce
attributes:
label: How to reproduce it (minimal and precise)
placeholder: First do this, than this..
validations:
required: false
- type: input
id: z2m_version
attributes:
label: Zigbee2MQTT version
description: Can be found in the frontend -> settings -> about -> Zigbee2MQTT version. Are you running Zigbee2MQTT 1.18.1? Then read [this](https://github.com/Koenkk/zigbee2mqtt/releases/tag/1.19.0).
placeholder: '1.22.1'
validations:
required: true
- type: input
id: adapter_fwversion
attributes:
label: Adapter firmware version
description: Can be found in the frontend -> settings -> about -> coordinator revision
placeholder: '20211210'
validations:
required: true
- type: input
id: adapter
attributes:
label: Adapter
description: The adapter you are using
placeholder: Electrolama zig-a-zig-ah! (zzh!), Slaesh's CC2652RB stick, ...
validations:
required: true
- type: textarea
id: log
attributes:
label: Debug log
description: After enabling [debug logging](https://www.zigbee2mqtt.io/guide/usage/debug.html) the log can be found under `data/log`. Attach the file below
placeholder: Click here and drag the file into it or click on "Attach files by.." below
validations:
required: false
+64
View File
@@ -0,0 +1,64 @@
name: Wrong device picture/vendor/model/description
description: Use if device is detected as supported and is fully functional but has a wrong picture, vendor, model or description
title: "[Wrong device]: "
labels: [wrong device]
body:
- type: markdown
attributes:
value: |
Only use this if the device is detected as **supported** and is **fully functional** but has a wrong picture, vendor, model or description.
- type: input
id: link
attributes:
label: Link
description: Link of this device (product page)
placeholder: https://www.linktomydevice.org
validations:
required: true
- type: input
id: model
attributes:
label: Model
description: Expected model, model that is printed on the device, for TuYa device this is NOT something like TS0601 or _TZE200_cf1sl3tj
placeholder: RTCGQ01LM
validations:
required: true
- type: input
id: description
attributes:
label: Description
description: Expected description
placeholder: Motion sensor
validations:
required: true
- type: input
id: vendor
attributes:
label: Vendor
description: Expected vendor
placeholder: Xiaomi
validations:
required: true
- type: input
id: picture
attributes:
label: Picture (link)
description: Expected picture
placeholder: https://www.linktomydevice.org/RTCGQ01LM.jpg
validations:
required: true
- type: input
id: database
attributes:
label: Database entry
description: Entry of this device in `data/database.db` after pairing it
placeholder: '{"id":53,"type":"Router","ieeeAddr":"0x10458d00024284f69","nwkAddr":10148,"manufId":4151,"manufName":"LUMI","powerSource":"DC Source","modelId":"lumi.relay.c2acn01","epList":[1,2],"endpoints":{"1":{"profId":260,"epId":1,"devId":257,"inClusterList":[0,3,4,5,1,2,10,6,16,2820,12],"outClusterList":[25,10],"clusters":{"genBasic":{"attributes":{"modelId":"lumi.relay.c2acn01","appVersion":1,"manufacturerName":"LUMI","powerSource":4,"zclVersion":0,"stackVersion":2,"hwVersion":18,"dateCode":"8-6-2020"}},"genAnalogInput":{"attributes":{"presentValue":129.04425048828125}},"genOnOff":{"attributes":{"61440":117440715,"onOff":1}}},"binds":[],"configuredReportings":[],"meta":{}},"2":{"profId":260,"epId":2,"devId":257,"inClusterList":[6,16,4,5],"outClusterList":[],"clusters":{"genOnOff":{"attributes":{"61440":237478966,"onOff":0}}},"binds":[],"configuredReportings":[],"meta":{}}},"appVersion":1,"stackVersion":2,"hwVersion":18,"dateCode":"8-6-2020","zclVersion":0,"interviewCompleted":true,"meta":{},"lastSeen":1640285631405}'
validations:
required: true
- type: textarea
id: notes
attributes:
label: Notes
placeholder: Some additional notes...
validations:
required: false
-1
View File
@@ -1 +0,0 @@
preferredBranch: dev
+13
View File
@@ -0,0 +1,13 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
target-branch: dev
- package-ecosystem: "docker"
directory: "/docker"
schedule:
interval: "daily"
target-branch: dev
+45 -27
View File
@@ -1,28 +1,22 @@
name: CI
name: ci
on: [push, pull_request]
on: [pull_request, push]
permissions:
contents: write
pull-requests: write
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2-beta
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 14
node-version: 20
registry-url: https://registry.npmjs.org/
- name: Get NodeJS Version
id: nodeversion
run: |
echo "::set-output name=version::$(node --version)"
- name: Restore node_modules cache
id: cache-node-modules
uses: actions/cache@v2
with:
path: node_modules
key: ${{ runner.os }}-${{ steps.nodeversion.outputs.version }}-${{ hashFiles('npm-shrinkwrap.json') }}
cache: npm
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm ci
- name: Build
run: npm run build
@@ -35,16 +29,16 @@ jobs:
run: echo ${{ secrets.DOCKER_KEY }} | docker login -u koenkk --password-stdin
- name: Docker login ghcr.io
if: (github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push'
run: echo ${{ secrets.CR_PAT }} | docker login ghcr.io -u koenkk --password-stdin
run: echo ${{ secrets.GH_TOKEN }} | docker login ghcr.io -u koenkk --password-stdin
- name: Docker setup - QEMU
if: (github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push'
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v2
with:
platforms: all
- name: Docker setup - Buildx
if: (github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push'
id: buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v2
with:
version: latest
- name: Docker build dev
@@ -52,8 +46,9 @@ jobs:
run: |
docker buildx build \
--build-arg COMMIT=$(git rev-parse --short HEAD) \
--platform linux/arm64/v8,linux/amd64,linux/arm/v6,linux/arm/v7 \
--platform linux/arm64/v8,linux/386,linux/amd64,linux/arm/v6,linux/arm/v7 \
-f docker/Dockerfile \
--provenance=false \
--push \
-t koenkk/zigbee2mqtt:latest-dev -t ghcr.io/koenkk/zigbee2mqtt:latest-dev \
.
@@ -63,8 +58,9 @@ jobs:
TAG="$(git describe --tags)"
docker buildx build \
--build-arg COMMIT=$(git rev-parse --short HEAD) \
--platform linux/arm64/v8,linux/amd64,linux/arm/v6,linux/arm/v7 \
--platform linux/arm64/v8,linux/386,linux/amd64,linux/arm/v6,linux/arm/v7 \
-f docker/Dockerfile \
--provenance=false \
--push \
-t koenkk/zigbee2mqtt:latest -t "koenkk/zigbee2mqtt:$TAG" -t ghcr.io/koenkk/zigbee2mqtt:latest -t "ghcr.io/koenkk/zigbee2mqtt:$TAG" \
.
@@ -76,9 +72,31 @@ jobs:
- name: Trigger zigbee2mqtt/hassio-zigbee2mqtt build
if: github.ref == 'refs/heads/dev' && github.event_name == 'push'
run: |
curl -s \
--request POST \
--header "Content-Type: application/json" \
--data '{"definition": {"id": 9}}' \
-u ${{ secrets.HASSIO_TRIGGER_SECRET }} \
"https://dev.azure.com/zigbee2mqtt/Zigbee2mqtt%20Add-on/_apis/build/builds?api-version=5.1"
curl \
-X POST \
-H "Authorization: token ${{ secrets.GH_TOKEN }}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/zigbee2mqtt/hassio-zigbee2mqtt/actions/workflows/ci.yml/dispatches \
-d '{"ref":"master","inputs":{}}'
tests:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node: [16, 18, 20]
runs-on: ${{ matrix.os }}
continue-on-error: true
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
registry-url: https://registry.npmjs.org/
cache: 'npm'
- name: Install dependencies
# --ignore-scripts prevents the serialport build which often fails on Windows
run: npm ci --ignore-scripts
- name: Build
run: npm run build
- name: Test
run: npm run test-with-coverage
+23
View File
@@ -0,0 +1,23 @@
name: codeql
on:
push:
branches:
- dev
jobs:
CodeQL-Build:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
+14
View File
@@ -0,0 +1,14 @@
name: deps-review
on: [pull_request]
permissions:
contents: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- name: 'Checkout repository'
uses: actions/checkout@v3
- name: 'Dependency review'
uses: actions/dependency-review-action@v3
+34
View File
@@ -0,0 +1,34 @@
on:
workflow_dispatch:
name: ghcr-cleanup
permissions: {}
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Delete untagged images
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GH_TOKEN }}
script: |
const response = await github.request("GET /${{ env.OWNER }}/packages/container/${{ env.PACKAGE_NAME }}/versions",
{ per_page: ${{ env.PER_PAGE }}
});
for(version of response.data) {
if (version.metadata.container.tags.length == 0) {
try {
console.log("delete " + version.id)
const deleteResponse = await github.request("DELETE /${{ env.OWNER }}/packages/container/${{ env.PACKAGE_NAME }}/versions/" + version.id, { });
console.log("status " + deleteResponse.status)
} catch (e) {
console.log("failed")
}
}
}
env:
OWNER: user
PACKAGE_NAME: zigbee2mqtt
PER_PAGE: 2000
+19
View File
@@ -0,0 +1,19 @@
name: merge-master-to-dev
on:
push:
branches:
- master
jobs:
merge_master_to_dev:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: devmasx/merge-branch@master
with:
type: now
head_to_merge: master
target_branch: dev
message: "chore: merge master to dev"
github_token: ${{ secrets.GH_TOKEN }}
+115
View File
@@ -0,0 +1,115 @@
on:
push:
branches:
- dev
permissions:
contents: write
pull-requests: write
name: release-please
jobs:
release-please:
runs-on: ubuntu-latest
outputs:
release_created: ${{ steps.release.outputs.release_created }}
version: "${{steps.release.outputs.major}}.${{steps.release.outputs.minor}}.${{steps.release.outputs.patch}}"
steps:
- uses: actions/setup-node@v3
with:
node-version: 20
- uses: google-github-actions/release-please-action@v3
id: release
with:
release-type: node
package-name: release-please-action
default-branch: dev
token: ${{secrets.GH_TOKEN}}
include-v-in-tag: false
skip-github-release: true
# Checkout repos
- uses: actions/checkout@v3
with:
repository: koenkk/zigbee2mqtt
path: ./z2m
- uses: actions/checkout@v3
with:
repository: koenkk/zigbee2mqtt
path: ./z2m-master
ref: master
- name: Update latest-dev tag
run: |
cd z2m
git push --delete origin latest-dev || true
git tag latest-dev
git push origin latest-dev
- name: Cache commit-user-lookup.json
uses: actions/cache@v3
with:
path: z2m/scripts/commit-user-lookup.json
key: always
- name: Update latest-dev release changelog
run: |
MASTER_Z2M_VERSION=$(cat z2m-master/package.json | jq -r '.version')
MASTER_ZHC_VERSION=$(cat z2m-master/package.json | jq -r '.dependencies."zigbee-herdsman-converters"')
MASTER_ZH_VERSION=$(cat z2m-master/package.json | jq -r '.dependencies."zigbee-herdsman"')
wget -q -O - https://raw.githubusercontent.com/Koenkk/zigbee2mqtt/release-please--branches--dev--components--release-please-action/CHANGELOG.md > z2m/CHANGELOG.md || true
cd z2m
npm ci
node scripts/generateChangelog.js $MASTER_Z2M_VERSION $MASTER_ZHC_VERSION $MASTER_ZH_VERSION >> ../changelog.txt
env:
GH_TOKEN: ${{secrets.GH_TOKEN}}
- uses: ncipollo/release-action@v1
with:
bodyFile: changelog.txt
prerelease: true
name: latest-dev
allowUpdates: true
tag: latest-dev
- name: Upload changelog
uses: actions/upload-artifact@v3
with:
name: changelog
path: changelog.txt
create_release:
runs-on: ubuntu-latest
needs: [release-please]
if: ${{ needs.release-please.outputs.release_created }}
steps:
- uses: actions/checkout@v3
with:
ref: dev
token: ${{secrets.GH_TOKEN}}
- name: Download changelog
uses: actions/download-artifact@v3
with:
name: changelog
path: ../changelog
- uses: ncipollo/release-action@v1
name: Create release
with:
bodyFile: ../changelog/changelog.txt
draft: true
name: ${{ needs.release-please.outputs.version }}
tag: ${{ needs.release-please.outputs.version }}
- name: Merge dev -> master
uses: devmasx/merge-branch@master
with:
type: now
from_branch: dev
target_branch: master
github_token: ${{secrets.GH_TOKEN}}
- name: Promote to dev
run: |
jq '.version = "${{ needs.release-please.outputs.version }}-dev"' package.json > package.json.tmp
jq '.version = "${{ needs.release-please.outputs.version }}-dev"' package-lock.json > package-lock.json.tmp
mv package.json.tmp package.json
mv package-lock.json.tmp package-lock.json
- uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: "chore: promote to dev"
+4 -3
View File
@@ -1,4 +1,5 @@
name: "Close stale issues/pull requests"
name: stale
on:
schedule:
- cron: "0 0 * * *"
@@ -8,12 +9,12 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3
- uses: actions/stale@v8
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'
days-before-stale: 30
days-before-close: 7
exempt-issue-labels: dont-stale
operations-per-run: 500
exempt-issue-labels: 'breaking change'
+38
View File
@@ -0,0 +1,38 @@
on:
repository_dispatch:
types: update_dep
name: update-dep
permissions: {}
jobs:
update_dep:
permissions:
contents: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: dev
token: ${{ secrets.GH_TOKEN }}
- uses: actions/setup-node@v3
with:
node-version: 20
cache: npm
- run: npm install ${{ github.event.client_payload.package }}@${{ github.event.client_payload.version }} --save-exact
- uses: peter-evans/create-pull-request@v5
id: cpr
with:
commit-message: "fix(ignore): update ${{ github.event.client_payload.package }} to ${{ github.event.client_payload.version }}"
branch: "deps/${{ github.event.client_payload.package }}"
title: Update ${{ github.event.client_payload.package }} to ${{ github.event.client_payload.version }}
token: ${{ secrets.GH_TOKEN }}
- run: sleep 5 # Otherwise pull request may not exist yet causing automerge to fail
- uses: peter-evans/enable-pull-request-automerge@v3
if: steps.cpr.outputs.pull-request-operation == 'created'
with:
token: ${{ secrets.GH_TOKEN }}
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
merge-method: squash
+17 -10
View File
@@ -4,22 +4,29 @@ on:
schedule:
- cron: "0 0 * * 0"
permissions:
contents: read
jobs:
update_deps:
permissions:
contents: write # for peter-evans/create-pull-request to create branch
pull-requests: write # for peter-evans/create-pull-request to create a PR
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
ref: dev
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
- uses: actions/setup-node@v2-beta
token: ${{ secrets.GH_TOKEN }}
- uses: actions/setup-node@v3
with:
node-version: 14
- run: rm -rf npm-shrinkwrap.json
- run: npx npm-check-updates -u
- run: npm install && npm shrinkwrap --dev
- uses: peter-evans/create-pull-request@v3
node-version: 20
cache: npm
- run: npx npm-check-updates -u -x connect-gzip-static # connect-gzip-static only supports node 16
- run: npm install
- uses: peter-evans/create-pull-request@v5
with:
commit-message: Update dependencies
branch: "update_deps"
commit-message: "fix(ignore): update dependencies"
branch: "deps/all"
title: Update dependencies
token: ${{ secrets.GH_TOKEN }}
-22
View File
@@ -1,22 +0,0 @@
name: Update zigbee-frontend
on:
repository_dispatch:
types: update_frontend
jobs:
update_frontend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: dev
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
- uses: actions/setup-node@v2-beta
with:
node-version: 14
- run: npm install zigbee2mqtt-frontend@${{ github.event.client_payload.version }} --save-exact
- uses: peter-evans/create-pull-request@v3
with:
commit-message: Update zigbee2mqtt-frontend to ${{ github.event.client_payload.version }}
branch: "update_frontend"
title: Update zigbee-frontend to ${{ github.event.client_payload.version }}
-22
View File
@@ -1,22 +0,0 @@
name: Update zigbee-herdsman
on:
repository_dispatch:
types: update_zh
jobs:
update_zh:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: dev
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
- uses: actions/setup-node@v2-beta
with:
node-version: 14
- run: npm install zigbee-herdsman@${{ github.event.client_payload.version }} --save-exact
- uses: peter-evans/create-pull-request@v3
with:
commit-message: Update zigbee-herdsman to ${{ github.event.client_payload.version }}
branch: "update_zh"
title: Update zigbee-herdsman to ${{ github.event.client_payload.version }}
-22
View File
@@ -1,22 +0,0 @@
name: Update zigbee-herdsman-converters
on:
repository_dispatch:
types: update_zhc
jobs:
update_zhc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: dev
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
- uses: actions/setup-node@v2-beta
with:
node-version: 14
- run: npm install zigbee-herdsman-converters@${{ github.event.client_payload.version }} --save-exact
- uses: peter-evans/create-pull-request@v3
with:
commit-message: Update zigbee-herdsman-converters to ${{ github.event.client_payload.version }}
branch: "update_zhc"
title: Update zigbee-herdsman-converters to ${{ github.event.client_payload.version }}
+3
View File
@@ -80,3 +80,6 @@ data/extension
# Ignore config
data/*.yaml
# commit-user-lookup.json
scripts/commit-user-lookup.json
+4
View File
@@ -0,0 +1,4 @@
{
".": "1.32.1",
"bootstrap-sha": "b2dd21e164b457d5cd0c9d455cdb879d881c44bd"
}
+70
View File
@@ -0,0 +1,70 @@
# Changelog
## [1.32.1](https://github.com/Koenkk/zigbee2mqtt/compare/1.32.0...1.32.1) (2023-07-08)
### Bug Fixes
* **ignore:** update zigbee-herdsman-converters to 15.33.1-hotfix.0 ([15601d6](https://github.com/Koenkk/zigbee2mqtt/commit/15601d6339cfc7408872a71071a98abadd9b33ff))
## [1.32.0](https://github.com/Koenkk/zigbee2mqtt/compare/1.31.2...1.32.0) (2023-07-01)
### Features
* Support Home Assistant water device_class ([#18066](https://github.com/Koenkk/zigbee2mqtt/issues/18066)) ([e083a6b](https://github.com/Koenkk/zigbee2mqtt/commit/e083a6bff7fafd17d5ff6ad4b87916b83727d8be))
* Support settable Home Assistant text ([#18114](https://github.com/Koenkk/zigbee2mqtt/issues/18114)) ([9474452](https://github.com/Koenkk/zigbee2mqtt/commit/94744525ba466c6512ab6a6181b3073970a02023))
### Bug Fixes
* Fix Home Assistant truncate error when `program` is `null`. https://github.com/Koenkk/zigbee2mqtt/issues/16460 ([11f0be5](https://github.com/Koenkk/zigbee2mqtt/commit/11f0be55d871363cb4c07adbbeac57fb4ddf479e))
* **ignore:** Better fix for [#17891](https://github.com/Koenkk/zigbee2mqtt/issues/17891) ([#17951](https://github.com/Koenkk/zigbee2mqtt/issues/17951)) ([a7e02a7](https://github.com/Koenkk/zigbee2mqtt/commit/a7e02a7be847418df463095b68951c1a1459ca83))
* **ignore:** Fix DeviceMessage.meta type ([8833947](https://github.com/Koenkk/zigbee2mqtt/commit/88339473ce7eb09add8f1836ab4c33b4a405d7e7))
* **ignore:** Fix e10dd893ab2875e5420952b4452bf75aeec93922 ([446dbbf](https://github.com/Koenkk/zigbee2mqtt/commit/446dbbf1fc0e07c61cd233c8b4ea509ce6423c86))
* **ignore:** update dependencies ([#17991](https://github.com/Koenkk/zigbee2mqtt/issues/17991)) ([e10dd89](https://github.com/Koenkk/zigbee2mqtt/commit/e10dd893ab2875e5420952b4452bf75aeec93922))
* **ignore:** update dependencies ([#18052](https://github.com/Koenkk/zigbee2mqtt/issues/18052)) ([1dc0b24](https://github.com/Koenkk/zigbee2mqtt/commit/1dc0b24e9a8ae9a03809aff5d3ff9758b63d9f22))
* **ignore:** update dependencies ([#18112](https://github.com/Koenkk/zigbee2mqtt/issues/18112)) ([57566c7](https://github.com/Koenkk/zigbee2mqtt/commit/57566c76ed8ccaabcdc32f84a7fddcf5da3b02b3))
* **ignore:** update zigbee-herdsman to 0.15.0 ([#17966](https://github.com/Koenkk/zigbee2mqtt/issues/17966)) ([05cab61](https://github.com/Koenkk/zigbee2mqtt/commit/05cab61e1faa15a9a72d87f8e9ec26e3d1eb4046))
* **ignore:** update zigbee-herdsman to 0.15.1 ([#18080](https://github.com/Koenkk/zigbee2mqtt/issues/18080)) ([17cff2a](https://github.com/Koenkk/zigbee2mqtt/commit/17cff2a26f16207bd30bb2659e11ce792d02dd75))
* **ignore:** update zigbee-herdsman to 0.15.2 ([#18091](https://github.com/Koenkk/zigbee2mqtt/issues/18091)) ([03005b3](https://github.com/Koenkk/zigbee2mqtt/commit/03005b38c86f79384e20bb240a8947ce969411c6))
* **ignore:** update zigbee-herdsman to 0.15.3 ([#18116](https://github.com/Koenkk/zigbee2mqtt/issues/18116)) ([2d62027](https://github.com/Koenkk/zigbee2mqtt/commit/2d62027e24ea2de401155e0bc7fabaee85fa7bab))
* **ignore:** update zigbee-herdsman to 0.16.0 ([#18124](https://github.com/Koenkk/zigbee2mqtt/issues/18124)) ([38c2ea5](https://github.com/Koenkk/zigbee2mqtt/commit/38c2ea5bf72f67f29b89dc84524e91923989fd27))
* **ignore:** update zigbee-herdsman-converters to 15.20.0 ([#17954](https://github.com/Koenkk/zigbee2mqtt/issues/17954)) ([0c7e7e5](https://github.com/Koenkk/zigbee2mqtt/commit/0c7e7e5089d1023150f23123df7eab90ea65fe59))
* **ignore:** update zigbee-herdsman-converters to 15.22.0 ([#18004](https://github.com/Koenkk/zigbee2mqtt/issues/18004)) ([b0b3aee](https://github.com/Koenkk/zigbee2mqtt/commit/b0b3aee6483efe9fbb2f434c44ed76d6b3a4e54a))
* **ignore:** update zigbee-herdsman-converters to 15.23.0 ([#18015](https://github.com/Koenkk/zigbee2mqtt/issues/18015)) ([4ad838c](https://github.com/Koenkk/zigbee2mqtt/commit/4ad838c114edc7dd5d9d104df9b2ce49933a8c20))
* **ignore:** update zigbee-herdsman-converters to 15.24.0 ([#18025](https://github.com/Koenkk/zigbee2mqtt/issues/18025)) ([c47f5df](https://github.com/Koenkk/zigbee2mqtt/commit/c47f5df021478e8997f13f06ea17591b745615e3))
* **ignore:** update zigbee-herdsman-converters to 15.25.0 ([#18056](https://github.com/Koenkk/zigbee2mqtt/issues/18056)) ([57aaa5a](https://github.com/Koenkk/zigbee2mqtt/commit/57aaa5a85aff308d4bd66353fd42969eb38a60d4))
* **ignore:** update zigbee-herdsman-converters to 15.26.0 ([#18067](https://github.com/Koenkk/zigbee2mqtt/issues/18067)) ([91d67a0](https://github.com/Koenkk/zigbee2mqtt/commit/91d67a0ef5135e08ac81171b78931b7903efcc5a))
* **ignore:** update zigbee-herdsman-converters to 15.27.0 ([#18075](https://github.com/Koenkk/zigbee2mqtt/issues/18075)) ([5777d76](https://github.com/Koenkk/zigbee2mqtt/commit/5777d76e5508d7dba4750aa5efc34977ba01de1a))
* **ignore:** update zigbee-herdsman-converters to 15.28.0 ([#18092](https://github.com/Koenkk/zigbee2mqtt/issues/18092)) ([6d3389b](https://github.com/Koenkk/zigbee2mqtt/commit/6d3389b409b003740ac4b2b90aa39a8ccbec26e3))
* **ignore:** update zigbee-herdsman-converters to 15.29.0 ([#18099](https://github.com/Koenkk/zigbee2mqtt/issues/18099)) ([a744838](https://github.com/Koenkk/zigbee2mqtt/commit/a744838e9c982461479f0b5273e25abd32c4074d))
* **ignore:** update zigbee-herdsman-converters to 15.30.0 ([#18115](https://github.com/Koenkk/zigbee2mqtt/issues/18115)) ([a56719d](https://github.com/Koenkk/zigbee2mqtt/commit/a56719d19ca32fabf375e293cdef0172826c8672))
* **ignore:** update zigbee-herdsman-converters to 15.31.0 ([#18125](https://github.com/Koenkk/zigbee2mqtt/issues/18125)) ([ed85367](https://github.com/Koenkk/zigbee2mqtt/commit/ed85367e5383b9a59a69fe2836b02763f6744849))
* **ignore:** update zigbee-herdsman-converters to 15.32.0 ([#18132](https://github.com/Koenkk/zigbee2mqtt/issues/18132)) ([61dd1dd](https://github.com/Koenkk/zigbee2mqtt/commit/61dd1dd96dc9154f2b46a635bf2ccaeac2d69ba5))
* **ignore:** update zigbee-herdsman-converters to 15.33.0 ([#18156](https://github.com/Koenkk/zigbee2mqtt/issues/18156)) ([81973c3](https://github.com/Koenkk/zigbee2mqtt/commit/81973c3a2a356c52859d167d3eedaf78f747b8e5))
* **ignore:** update zigbee-herdsman-converters to 15.33.1 ([#18161](https://github.com/Koenkk/zigbee2mqtt/issues/18161)) ([d1824f1](https://github.com/Koenkk/zigbee2mqtt/commit/d1824f169b71e0590dce14d7f451dbbb67bdf5d9))
## [1.31.0](https://github.com/Koenkk/zigbee2mqtt/compare/1.30.4...v1.31.0) (2023-06-01)
### Features
* **ignore:** update CI/CD ([b3de973](https://github.com/Koenkk/zigbee2mqtt/commit/b3de973af66e7c308987adeaab4d89759d4bfef0))
* **ignore:** update CI/CD ([0d17d26](https://github.com/Koenkk/zigbee2mqtt/commit/0d17d2613357db225f224bdbf191fbd9ad17b4a5))
* **ignore:** update zigbee-herdsman-converters to 15.6.0 ([#17774](https://github.com/Koenkk/zigbee2mqtt/issues/17774)) ([24baed3](https://github.com/Koenkk/zigbee2mqtt/commit/24baed3053d91276369d0cad02e96afee97329f8))
* **ignore:** update zigbee-herdsman-converters to 15.7.0 ([#17777](https://github.com/Koenkk/zigbee2mqtt/issues/17777)) ([1344ae0](https://github.com/Koenkk/zigbee2mqtt/commit/1344ae06163f5b6a3c68e532f39b379158040c6f))
### Bug Fixes
* Fix Home Assistant `Payload is not supported (e.g. open, closed, opening, closing, stopped): STOP` error. https://github.com/Koenkk/zigbee2mqtt/issues/17552 ([e6f9aed](https://github.com/Koenkk/zigbee2mqtt/commit/e6f9aeda06ec96c76c3d455bb016a10073d0bf54))
* **ignore:** Fix build ([#17838](https://github.com/Koenkk/zigbee2mqtt/issues/17838)) ([d119f60](https://github.com/Koenkk/zigbee2mqtt/commit/d119f609de683313335fbb37e2ce358a16fdc597))
* **ignore:** update dependencies ([#17811](https://github.com/Koenkk/zigbee2mqtt/issues/17811)) ([a71857c](https://github.com/Koenkk/zigbee2mqtt/commit/a71857c7236c66da577352160de96eacc683c7c3))
* **ignore:** update zigbee-herdsman to 0.14.117 ([#17770](https://github.com/Koenkk/zigbee2mqtt/issues/17770)) ([8457c37](https://github.com/Koenkk/zigbee2mqtt/commit/8457c3735b2f2c9c5106ce76fc7d2988f9eb1ea1))
* **ignore:** update zigbee-herdsman-converters to 15.10.0 ([#17797](https://github.com/Koenkk/zigbee2mqtt/issues/17797)) ([b7b3b51](https://github.com/Koenkk/zigbee2mqtt/commit/b7b3b51cfeb9b672f2cca128733b273f50966e19))
* **ignore:** update zigbee-herdsman-converters to 15.11.0 ([#17808](https://github.com/Koenkk/zigbee2mqtt/issues/17808)) ([2e8dfbb](https://github.com/Koenkk/zigbee2mqtt/commit/2e8dfbb96ecbebece84cc1aab12899162e27f82e))
* **ignore:** update zigbee-herdsman-converters to 15.12.0 ([#17837](https://github.com/Koenkk/zigbee2mqtt/issues/17837)) ([be82b73](https://github.com/Koenkk/zigbee2mqtt/commit/be82b737be5f7a7e58d653628ce66e0813fad111))
* **ignore:** update zigbee-herdsman-converters to 15.13.0 ([#17842](https://github.com/Koenkk/zigbee2mqtt/issues/17842)) ([b228d9d](https://github.com/Koenkk/zigbee2mqtt/commit/b228d9daf64c8d315803120814dc31272ee1c459))
* **ignore:** update zigbee-herdsman-converters to 15.13.1 ([#17851](https://github.com/Koenkk/zigbee2mqtt/issues/17851)) ([4b20dfa](https://github.com/Koenkk/zigbee2mqtt/commit/4b20dfafab6193f328aa9133d2b063284abf6cbf))
* **ignore:** update zigbee-herdsman-converters to 15.8.2 ([#17789](https://github.com/Koenkk/zigbee2mqtt/issues/17789)) ([8b579b6](https://github.com/Koenkk/zigbee2mqtt/commit/8b579b6533876f3b7440dc74b2b709cbfc7ccaf3))
* **ignore:** update zigbee-herdsman-converters to 15.9.0 ([#17791](https://github.com/Koenkk/zigbee2mqtt/issues/17791)) ([5b807c3](https://github.com/Koenkk/zigbee2mqtt/commit/5b807c36eced55e790a8393667e07680736f37be))
+3 -3
View File
@@ -3,6 +3,6 @@
Everybody is invited and welcome to contribute to Zigbee2MQTT. Zigbee2MQTT is writen in JavaScript and is based upon [zigbee-herdsman](https://github.com/koenkk/zigbee-herdsman) and [zigbee-herdsman-converters](https://github.com/koenkk/zigbee-herdsman-converters). Zigbee-herdsman-converters contains all device definition, zigbee-herdsman is responsible for handling all communication with the adapter.
- Pull requests are always created against the [**dev**](https://github.com/Koenkk/zigbee2mqtt/tree/dev) branch.
- Easiest way to start developing Zigbee2MQTT is by setting up a development environment (aka bare-metal installation). You can follow this [guide](https://www.zigbee2mqtt.io/getting_started/running_zigbee2mqtt.html#running-zigbee2mqtt) to do this.
- You can run the tests locally by executing `npm test`. Zigbee2MQTT enforces 100% code coverage, in case you add new code check if you code is covered by running `npm run test-with-coverage`. The coverage report can be found under `coverage/lcov-report/index.html`. Linting is also enforced and can be run with `npm run eslint`.
- When you want to add support for a new devices no changes to Zigbee2MQTT have to be made, only to zigbee-herdsman-converters. You can find a guide for it [here](https://www.zigbee2mqtt.io/how_tos/how_to_support_new_devices.html).
- Easiest way to start developing Zigbee2MQTT is by setting up a development environment (aka bare-metal installation). You can follow this [guide](https://www.zigbee2mqtt.io/guide/installation/01_linux.html) to do this.
- You can run the tests locally by executing `npm test`. Zigbee2MQTT enforces 100% code coverage, in case you add new code check if your code is covered by running `npm run test-with-coverage`. The coverage report can be found under `coverage/lcov-report/index.html`. Linting is also enforced and can be run with `npm run eslint`.
- When you want to add support for a new devices no changes to Zigbee2MQTT have to be made, only to zigbee-herdsman-converters. You can find a guide for it [here](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html).
+26 -9
View File
@@ -35,26 +35,36 @@
</div>
<h1>Zigbee2MQTT 🌉 🐝</h1>
<p>
Allows you to use your Zigbee devices <b>without</b> the vendors bridge or gateway.
Allows you to use your Zigbee devices <b>without</b> the vendor's bridge or gateway.
</p>
<p>
It bridges events and allows you to control your Zigbee devices via MQTT. In this way you can integrate your Zigbee devices with whatever smart home infrastructure you are using.
</p>
</div>
## [Getting started](https://www.zigbee2mqtt.io/#getting-started)
## [Getting started](https://www.zigbee2mqtt.io/guide/getting-started)
The [documentation](https://www.zigbee2mqtt.io/) provides you all the information needed to get up and running! Make sure you don't skip sections if this is your first visit, as there might be important details in there for you.
If you aren't familiar with **Zigbee** terminology make sure you [read this](https://www.zigbee2mqtt.io/information/zigbee_network.html) to help you out.
If you aren't familiar with **Zigbee** terminology make sure you [read this](https://www.zigbee2mqtt.io/advanced/zigbee/01_zigbee_network.html) to help you out.
## [Integrations](https://www.zigbee2mqtt.io/#integration)
## [Integrations](https://www.zigbee2mqtt.io/guide/usage/integrations.html)
Zigbee2MQTT integrates well with (almost) every home automation solution because it uses MQTT. However the following integrations are worth mentioning:
<img align="left" height="100px" width="100px" src="https://user-images.githubusercontent.com/7738048/40914297-49e6e560-6800-11e8-8904-36cce896e5a8.png">
### [Home Assistant](https://www.home-assistant.io/)
- [Home Assistant](https://www.home-assistant.io/hassio/): Using [the official addon](https://github.com/zigbee2mqtt/hassio-zigbee2mqtt)
- Generic install or Hassbian: Using instructions [here](https://www.zigbee2mqtt.io/integration/home_assistant.html)
- [Home Assistant OS](https://www.home-assistant.io/installation/): Using [the official addon](https://github.com/zigbee2mqtt/hassio-zigbee2mqtt)
- Other installation: using instructions [here](https://www.zigbee2mqtt.io/guide/usage/integrations/home_assistant.html)
<br>
<img align="left" height="100px" width="100px" src="https://etc.athom.com/logo/white/256.png">
### [Homey](https://homey.app/)
- Integration implemented in the [Homey App](https://homey.app/nl-nl/app/com.gruijter.zigbee2mqtt/)
- Documentation and support in the [Homey Forum](https://community.homey.app/t/83214)
<br>
<img align="left" height="100px" width="100px" src="https://user-images.githubusercontent.com/2734836/47615848-b8dd8700-dabd-11e8-9d77-175002dd8987.png">
@@ -63,19 +73,26 @@ Zigbee2MQTT integrates well with (almost) every home automation solution because
<br>
<img align="left" height="100px" width="100px" src="./images/gladys-assistant-logo.jpg">
### [Gladys Assistant](https://gladysassistant.com/)
- Integration implemented natively in Gladys Assistant. Read documentation [here](https://gladysassistant.com/docs/integrations/zigbee2mqtt/)
<br>
## Architecture
![Architecture](images/architecture.png)
### Internal Architecture
Zigbee2MQTT is made up of three modules, each developed in its own Github project. Starting from the hardware (adapter) and moving up; [zigbee-herdsman](https://github.com/koenkk/zigbee-herdsman) connects to your Zigbee adapter an makes an API available to the higher levels of the stack. For e.g. Texas Instruments hardware, zigbee-herdsman uses the [TI zStack monitoring and test API](https://github.com/koenkk/zigbee-herdsman/raw/master/docs/Z-Stack%20Monitor%20and%20Test%20API.pdf) to communicate with the adapter. Zigbee-herdsman handles the core Zigbee communication. The module [zigbee-herdsman-converters](https://github.com/koenkk/zigbee-herdsman-converters) handles the mapping from individual device models to the Zigbee clusters they support. [Zigbee clusters](https://github.com/Koenkk/zigbee-herdsman/raw/master/docs/Zigbee%20Cluster%20Library%20Specification%20v7.pdf) are the layers of the Zigbee protocol on top of the base protocol that define things like how lights, sensors and switches talk to each other over the Zigbee network. Finally, the Zigbee2MQTT module drives zigbee-herdsman and maps the zigbee messages to MQTT messages. Zigbee2MQTT also keeps track of the state of the system. It uses a `database.db` file to store this state; a text file with a JSON database of connected devices and their capabilities.
Zigbee2MQTT is made up of three modules, each developed in its own Github project. Starting from the hardware (adapter) and moving up; [zigbee-herdsman](https://github.com/koenkk/zigbee-herdsman) connects to your Zigbee adapter and makes an API available to the higher levels of the stack. For e.g. Texas Instruments hardware, zigbee-herdsman uses the [TI zStack monitoring and test API](https://github.com/koenkk/zigbee-herdsman/raw/master/docs/Z-Stack%20Monitor%20and%20Test%20API.pdf) to communicate with the adapter. Zigbee-herdsman handles the core Zigbee communication. The module [zigbee-herdsman-converters](https://github.com/koenkk/zigbee-herdsman-converters) handles the mapping from individual device models to the Zigbee clusters they support. [Zigbee clusters](https://github.com/Koenkk/zigbee-herdsman/blob/master/docs/07-5123-08-Zigbee-Cluster-Library.pdf) are the layers of the Zigbee protocol on top of the base protocol that define things like how lights, sensors and switches talk to each other over the Zigbee network. Finally, the Zigbee2MQTT module drives zigbee-herdsman and maps the zigbee messages to MQTT messages. Zigbee2MQTT also keeps track of the state of the system. It uses a `database.db` file to store this state; a text file with a JSON database of connected devices and their capabilities. Zigbee2MQTT provides a [web-based interface](https://github.com/nurikk/zigbee2mqtt-frontend) that allows monitoring and configuration.
### Developing
Zigbee2MQTT uses TypeScript (partially for now). Therefore after making changes to files in the `lib/` directory you need to recompile Zigbee2MQTT. This can be done by executing `npm run build`. For faster development instead of running `npm run build` you can run `npm run build-watch` in another terminal session, this will recompile as you change files.
## Supported devices
See [Supported devices](https://www.zigbee2mqtt.io/information/supported_devices.html) to check whether your device is supported. There is quite an extensive list, including devices from vendors like Xiaomi, Ikea, Philips, OSRAM and more.
See [Supported devices](https://www.zigbee2mqtt.io/supported-devices) to check whether your device is supported. There is quite an extensive list, including devices from vendors like [Xiaomi](https://www.zigbee2mqtt.io/supported-devices/#v=Xiaomi), [Ikea](https://www.zigbee2mqtt.io/supported-devices/#v=IKEA), [Philips](https://www.zigbee2mqtt.io/supported-devices/#v=Philips), [OSRAM](https://www.zigbee2mqtt.io/supported-devices/#v=OSRAM) and more.
If it's not listed in [Supported devices](https://www.zigbee2mqtt.io/information/supported_devices.html), support can be added (fairly) easy, see [How to support new devices](https://www.zigbee2mqtt.io/how_tos/how_to_support_new_devices.html).
If it's not listed in [Supported devices](https://www.zigbee2mqtt.io/supported-devices), support can be added (fairly) easily, see [How to support new devices](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html).
## Support & help
If you need assistance you can check [opened issues](https://github.com/Koenkk/zigbee2mqtt/issues). Feel free to help with Pull Requests when you were able to fix things or add new devices or just share the love on social media.
+10 -11
View File
@@ -1,29 +1,26 @@
FROM node:14-alpine3.12 as base
FROM alpine:3.18.2 as base
WORKDIR /app
RUN apk add --no-cache tzdata eudev tini
COPY package.json ./
RUN apk add --no-cache tzdata eudev tini nodejs
# Dependencies and build
FROM base as dependencies_and_build
COPY npm-shrinkwrap.json tsconfig.json index.js ./
COPY package*.json tsconfig.json index.js ./
COPY lib ./lib
RUN apk add --no-cache --virtual .buildtools make gcc g++ python3 linux-headers git && \
npm ci --no-audit --no-optional --no-update-notifier && \
npm run build && \
rm -rf node_modules && \
RUN apk add --no-cache --virtual .buildtools make gcc g++ python3 linux-headers git npm && \
npm ci --production --no-audit --no-optional --no-update-notifier && \
# Serialport needs to be rebuild for Alpine https://serialport.io/docs/9.x.x/guide-installation#alpine-linux
npm rebuild --build-from-source && \
apk del .buildtools
# Release
FROM base as release
COPY --from=dependencies_and_build /app/node_modules ./node_modules
COPY --from=dependencies_and_build /app/dist ./dist
COPY LICENSE index.js data/configuration.yaml ./
COPY dist ./dist
COPY package.json LICENSE index.js data/configuration.yaml ./
COPY docker/docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
@@ -33,5 +30,7 @@ RUN mkdir /app/data
ARG COMMIT
RUN echo "$COMMIT" > dist/.hash
ENV NODE_ENV production
ENTRYPOINT ["docker-entrypoint.sh"]
CMD [ "/sbin/tini", "--", "node", "index.js"]
Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

+32 -14
View File
@@ -1,10 +1,10 @@
const semver = require('semver');
const engines = require('./package.json').engines;
const indexJsRestart = 'indexjs.restart';
const fs = require('fs');
const os = require('os');
const path = require('path');
const {exec} = require('child_process');
const rimraf = require('rimraf');
const {rimrafSync} = require('rimraf');
require('source-map-support').install();
let controller;
@@ -13,12 +13,12 @@ let stopping = false;
const hashFile = path.join(__dirname, 'dist', '.hash');
async function restart() {
await stop(indexJsRestart);
await stop(true);
await start();
}
async function exit(code, reason) {
if (reason !== indexJsRestart) {
async function exit(code, restart) {
if (!restart) {
process.exit(code);
}
}
@@ -41,10 +41,22 @@ async function writeHash() {
async function build(reason) {
return new Promise((resolve, reject) => {
process.stdout.write(`Building Zigbee2MQTT... (${reason})`);
rimraf.sync('dist');
exec('npm run build', {cwd: __dirname}, async (err, stdout, stderr) => {
rimrafSync('dist');
const env = {...process.env};
const _600mb = 629145600;
if (_600mb > os.totalmem() && !env.NODE_OPTIONS) {
// Prevent OOM on tsc compile for system with low memory
// https://github.com/Koenkk/zigbee2mqtt/issues/12034
env.NODE_OPTIONS = '--max_old_space_size=256';
}
exec('npm run build', {env, cwd: __dirname}, async (err, stdout, stderr) => {
if (err) {
process.stdout.write(', failed\n');
if (err.code === 134) {
process.stderr.write(
'\n\nBuild failed; ran out-of-memory, free some memory (RAM) and start again\n\n');
}
reject(err);
} else {
process.stdout.write(', finished\n');
@@ -85,7 +97,7 @@ async function start() {
for (const error of errors) {
console.log(`- ${error}`);
}
console.log(`\nIf you don't know how to solve this, read https://www.zigbee2mqtt.io/information/configuration.html`); // eslint-disable-line
console.log(`\nIf you don't know how to solve this, read https://www.zigbee2mqtt.io/guide/configuration`); // eslint-disable-line
console.log(`\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n`);
exit(1);
}
@@ -95,21 +107,27 @@ async function start() {
await controller.start();
}
async function stop(reason=null) {
await controller.stop(reason);
async function stop(restart) {
await controller.stop(restart);
}
async function handleQuit() {
if (!stopping && controller) {
stopping = true;
await stop();
await stop(false);
}
}
if (process.argv.length === 3 && process.argv[2] === 'writehash') {
writeHash();
if (require.main === module || require.main.filename.endsWith(path.sep + 'cli.js')) {
if (process.argv.length === 3 && process.argv[2] === 'writehash') {
writeHash();
} else {
process.on('SIGINT', handleQuit);
process.on('SIGTERM', handleQuit);
start();
}
} else {
process.on('SIGINT', handleQuit);
process.on('SIGTERM', handleQuit);
start();
module.exports = {start};
}
-442
View File
@@ -1,442 +0,0 @@
const MQTT = require('./mqtt');
const Zigbee = require('./zigbee');
const EventBus = require('./eventBus');
const State = require('./state');
const logger = require('./util/logger');
const settings = require('./util/settings');
const utils = require('./util/utils');
const stringify = require('json-stable-stringify-without-jsonify');
const assert = require('assert');
// Extensions
const ExtensionFrontend = require('./extension/frontend');
const ExtensionPublish = require('./extension/publish');
const ExtensionReceive = require('./extension/receive');
const ExtensionNetworkMap = require('./extension/networkMap');
const ExtensionSoftReset = require('./extension/legacy/softReset');
const ExtensionHomeAssistant = require('./extension/homeassistant');
const ExtensionConfigure = require('./extension/configure');
const ExtensionDeviceGroupMembership = require('./extension/legacy/deviceGroupMembership');
const ExtensionBridgeLegacy = require('./extension/legacy/bridgeLegacy');
const ExtensionBridge = require('./extension/bridge');
const ExtensionGroups = require('./extension/groups');
const ExtensionAvailabilityLegacy = require('./extension/legacy/availability');
const ExtensionAvailability = require('./extension/availability');
const ExtensionBind = require('./extension/bind');
const ExtensionReport = require('./extension/legacy/report');
const ExtensionOnEvent = require('./extension/onEvent');
const ExtensionOTAUpdate = require('./extension/otaUpdate');
const ExtensionExternalConverters = require('./extension/externalConverters');
const ExtensionExternalExtension = require('./extension/externalExtension');
const AllExtensions = [
ExtensionPublish, ExtensionReceive, ExtensionNetworkMap, ExtensionSoftReset, ExtensionHomeAssistant,
ExtensionConfigure, ExtensionDeviceGroupMembership, ExtensionBridgeLegacy, ExtensionBridge, ExtensionGroups,
ExtensionAvailabilityLegacy, ExtensionBind, ExtensionReport, ExtensionOnEvent, ExtensionOTAUpdate,
ExtensionExternalConverters, ExtensionFrontend, ExtensionExternalExtension, ExtensionAvailability,
];
class Controller {
constructor(restartCallback, exitCallback) {
// eslint-disable-next-line
this.eventBus = new EventBus.default();
// eslint-disable-next-line
this.zigbee = new Zigbee.default(this.eventBus);
// eslint-disable-next-line
this.mqtt = new MQTT.default(this.eventBus);
this.state = new State(this.eventBus);
this.restartCallback = restartCallback;
this.exitCallback = exitCallback;
this.publishEntityState = this.publishEntityState.bind(this);
this.enableDisableExtension = this.enableDisableExtension.bind(this);
this.onZigbeeAdapterDisconnected = this.onZigbeeAdapterDisconnected.bind(this);
this.addExtension = this.addExtension.bind(this);
// Initialize extensions.
const args = [this.zigbee, this.mqtt, this.state, this.publishEntityState, this.eventBus,
this.enableDisableExtension, this.restartCallback, this.addExtension];
this.extensions = [
new ExtensionBridge(...args),
new ExtensionPublish(...args),
new ExtensionReceive(...args),
new ExtensionDeviceGroupMembership(...args),
new ExtensionConfigure(...args),
new ExtensionNetworkMap(...args),
new ExtensionGroups(...args),
new ExtensionBind(...args),
new ExtensionOnEvent(...args),
new ExtensionOTAUpdate(...args),
new ExtensionReport(...args),
];
if (settings.get().frontend) {
this.extensions.push(new ExtensionFrontend(...args));
}
if (settings.get().advanced.legacy_api) {
this.extensions.push(new ExtensionBridgeLegacy(...args));
}
if (settings.get().external_converters.length) {
this.extensions.push(new ExtensionExternalConverters(...args));
}
if (settings.get().homeassistant) {
this.extensions.push(new ExtensionHomeAssistant(...args));
}
/* istanbul ignore next */
if (settings.get().advanced.soft_reset_timeout !== 0) {
this.extensions.push(new ExtensionSoftReset(...args));
}
/* istanbul ignore else */
if (settings.get().experimental.availability_new) {
this.extensions.push(new ExtensionAvailability(...args));
} else {
if (settings.get().advanced.availability_timeout) {
this.extensions.push(new ExtensionAvailabilityLegacy(...args));
}
}
this.extensions.push(new ExtensionExternalExtension(...args));
}
async start() {
this.state.start();
logger.logOutput();
const info = await utils.getZigbee2MQTTVersion();
logger.info(`Starting Zigbee2MQTT version ${info.version} (commit #${info.commitHash})`);
// Start zigbee
let startResult;
try {
startResult = await this.zigbee.start();
this.callExtensionMethod('onZigbeeStarted', []);
this.eventBus.on('event', this.onZigbeeEvent.bind(this), this.constructor.name);
this.eventBus.onAdapterDisconnected(this, this.onZigbeeAdapterDisconnected);
} catch (error) {
logger.error('Failed to start zigbee');
// eslint-disable-next-line
logger.error('Check https://www.zigbee2mqtt.io/information/FAQ.html#help-zigbee2mqtt-fails-to-start for possible solutions');
logger.error('Exiting...');
logger.error(error.stack);
this.exitCallback(1);
}
// Disable some legacy options on new network creation
if (startResult === 'reset') {
settings.set(['advanced', 'homeassistant_legacy_entity_attributes'], false);
settings.set(['advanced', 'legacy_api'], false);
settings.set(['device_options', 'legacy'], false);
this.enableDisableExtension(false, 'BridgeLegacy');
}
// Log zigbee clients on startup
const devices = this.zigbee.getClientsLegacy();
logger.info(`Currently ${devices.length} devices are joined:`);
for (const device of devices) {
const entity = this.zigbee.resolveEntityLegacy(device);
const model = entity.definition ?
`${entity.definition.model} - ${entity.definition.vendor} ${entity.definition.description}` :
'Not supported';
logger.info(`${entity.name} (${entity.device.ieeeAddr}): ${model} (${entity.device.type})`);
}
// Enable zigbee join.
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.');
}
try {
await this.zigbee.permitJoinLegacy(settings.get().permit_join);
} catch (error) {
logger.error(`Failed to set permit join to ${settings.get().permit_join}`);
}
// MQTT
this.onMQTTMessage = this.onMQTTMessage.bind(this);
this.eventBus.onMQTTMessage(this.constructor.name, this.onMQTTMessage);
await this.mqtt.connect();
// Send all cached states.
if (settings.get().advanced.cache_state_send_on_startup && settings.get().advanced.cache_state) {
for (const device of this.zigbee.getClientsLegacy()) {
if (this.state.exists(device.ieeeAddr)) {
this.publishEntityState(device.ieeeAddr, this.state.get(device.ieeeAddr));
}
}
}
// Add devices which are in database but not in configuration to configuration
for (const device of this.zigbee.getClientsLegacy()) {
if (!settings.getDevice(device.ieeeAddr)) {
settings.addDevice(device.ieeeAddr);
}
}
// Call extensions
await this.callExtensionMethod('onMQTTConnected', []);
await this.callExtensionMethod('start', []);
}
async enableDisableExtension(enable, name) {
if (!enable) {
const extension = this.extensions.find((e) => e.constructor.name === name);
if (extension) {
await this.callExtensionMethod('stop', [], [extension]);
this.extensions.splice(this.extensions.indexOf(extension), 1);
}
} else {
const Extension = AllExtensions.find((e) => e.name === name);
assert(Extension, `Extension '${name}' does not exist`);
const extension = new Extension(this.zigbee, this.mqtt, this.state, this.publishEntityState, this.eventBus,
this.enableDisableExtension, this.restartCallback, this.addExtension);
this.extensions.push(extension);
await this.callExtensionMethod('onZigbeeStarted', [], [extension]);
await this.callExtensionMethod('onMQTTConnected', [], [extension]);
await this.callExtensionMethod('start', [], [extension]);
}
}
async addExtension(extension) {
this.extensions.push(extension);
await this.callExtensionMethod('onZigbeeStarted', [], [extension]);
await this.callExtensionMethod('onMQTTConnected', [], [extension]);
await this.callExtensionMethod('start', [], [extension]);
}
async stop(reason=null) {
// Call extensions
await this.callExtensionMethod('stop', []);
this.eventBus.removeListeners(this.constructor.name);
// Wrap-up
this.state.stop();
await this.mqtt.disconnect();
try {
await this.zigbee.stop();
logger.info('Stopped Zigbee2MQTT');
this.exitCallback(0, reason);
} catch (error) {
logger.error('Failed to stop Zigbee2MQTT');
this.exitCallback(1, reason);
}
}
async onZigbeeAdapterDisconnected() {
logger.error('Adapter disconnected, stopping');
await this.stop();
}
async onZigbeeEvent(type, data) {
const resolvedEntity = this.zigbee.resolveEntityLegacy(data.device || data.ieeeAddr);
if (data.device && data.device.type === 'Coordinator') {
logger.debug('Ignoring message from coordinator');
return;
}
if (data.device && !settings.getDevice(data.device.ieeeAddr)) {
// Only deviceLeave doesn't have a device (not interesting to add to settings)
resolvedEntity.settings = {...settings.get().device_options, ...settings.addDevice(data.device.ieeeAddr)};
}
const name = resolvedEntity && resolvedEntity.settings ? resolvedEntity.settings.friendlyName : null;
if (type === 'message') {
logger.debug(
`Received Zigbee message from '${name}', type '${data.type}', cluster '${data.cluster}'` +
`, data '${stringify(data.data)}' from endpoint ${data.endpoint.ID}` +
(data.hasOwnProperty('groupID') ? ` with groupID ${data.groupID}` : ``),
);
} else if (type === 'deviceJoined') {
logger.info(`Device '${name}' joined`);
} else if (type === 'deviceInterview') {
if (data.status === 'successful') {
logger.info(`Successfully interviewed '${name}', device has successfully been paired`);
if (resolvedEntity.definition) {
const {vendor, description, model} = resolvedEntity.definition;
logger.info(`Device '${name}' is supported, identified as: ${vendor} ${description} (${model})`);
} else {
logger.warn(
`Device '${name}' with Zigbee model '${data.device.modelID}' and manufacturer name ` +
`'${data.device.manufacturerName}' is NOT supported, ` +
`please follow https://www.zigbee2mqtt.io/how_tos/how_to_support_new_devices.html`,
);
}
} else if (data.status === 'failed') {
logger.error(`Failed to interview '${name}', device has not successfully been paired`);
} else {
/* istanbul ignore else */
if (data.status === 'started') {
logger.info(`Starting interview of '${name}'`);
}
}
} else if (type === 'deviceAnnounce') {
logger.debug(`Device '${name}' announced itself`);
} else if (type === 'deviceNetworkAddressChanged') {
logger.debug(`Device '${name}' changed network address`);
} else {
/* istanbul ignore else */
if (type === 'deviceLeave') {
const settingsDevice = settings.getDevice(data.ieeeAddr);
logger.warn(`Device '${settingsDevice ? settingsDevice.friendlyName : data.ieeeAddr}' ` +
`left the network`);
this.state.remove(data.ieeeAddr);
}
}
// Call extensions
await this.callExtensionMethod('onZigbeeEvent', [type, data, resolvedEntity]);
// In case this message is not handled by the receive extension and last_seen is enabled, publish
// a message to update the last_seen.
// TODO
// if (name && !result[ExtensionReceive.prototype.constructor.name] &&
// settings.get().advanced.last_seen !== 'disable' && type !== 'deviceLeave') {
// this.publishEntityState(name, {});
// }
}
onMQTTMessage(payload) {
const {topic, message} = payload;
logger.debug(`Received MQTT message on '${topic}' with data '${message}'`);
// Call extensions
this.callExtensionMethod('onMQTTMessage', [topic, message]);
}
async publishEntityState(IDorName, payload, stateChangeReason=null) {
const resolvedEntity = this.zigbee.resolveEntityLegacy(IDorName);
if (!resolvedEntity || !resolvedEntity.settings) {
logger.error(`'${IDorName}' does not exist, skipping publish`);
return;
}
let messagePayload = {...payload};
// Update state cache with new state.
const newState = this.state.set(resolvedEntity.settings.ID, payload, stateChangeReason);
if (settings.get().advanced.cache_state) {
// Add cached state to payload
messagePayload = newState;
}
const options = {
retain: utils.getObjectProperty(resolvedEntity.settings, 'retain', false),
qos: utils.getObjectProperty(resolvedEntity.settings, 'qos', 0),
};
const retention = utils.getObjectProperty(resolvedEntity.settings, 'retention', false);
if (retention !== false) {
options.properties = {messageExpiryInterval: retention};
}
const isDevice = resolvedEntity.type === 'device';
if (isDevice && settings.get().mqtt.include_device_information) {
const attributes = [
'ieeeAddr', 'networkAddress', 'type', 'manufacturerID', 'manufacturerName', 'powerSource',
'applicationVersion', 'stackVersion', 'zclVersion', 'hardwareVersion', 'dateCode', 'softwareBuildID',
];
messagePayload.device = {
friendlyName: resolvedEntity.name,
model: resolvedEntity.definition ? resolvedEntity.definition.model : 'unknown',
};
attributes.forEach((a) => messagePayload.device[a] = resolvedEntity.device[a]);
}
// Add lastseen
const lastSeen = settings.get().advanced.last_seen;
if (isDevice && lastSeen !== 'disable' && resolvedEntity.device.lastSeen) {
messagePayload.last_seen = utils.formatDate(resolvedEntity.device.lastSeen, lastSeen);
}
// Add device linkquality.
if (resolvedEntity.type === 'device' && resolvedEntity.device.linkquality !== undefined) {
messagePayload.linkquality = resolvedEntity.device.linkquality;
}
// filter mqtt message attributes
if (resolvedEntity.settings.filtered_attributes) {
resolvedEntity.settings.filtered_attributes.forEach((a) => delete messagePayload[a]);
}
for (const extension of this.extensions) {
if (extension.adjustMessagePayloadBeforePublish) {
extension.adjustMessagePayloadBeforePublish(resolvedEntity, messagePayload);
}
}
if (Object.entries(messagePayload).length) {
const output = settings.get().experimental.output;
if (output === 'attribute_and_json' || output === 'json') {
await this.mqtt.publish(resolvedEntity.name, stringify(messagePayload), options);
}
if (output === 'attribute_and_json' || output === 'attribute') {
await this.iteratePayloadAttributeOutput(`${resolvedEntity.name}/`, messagePayload, options);
}
}
this.eventBus.emit('publishEntityState', {messagePayload, entity: resolvedEntity, stateChangeReason, payload,
ieeeAddr: resolvedEntity.type === 'device' && resolvedEntity.device.ieeeAddr});
}
async iteratePayloadAttributeOutput(topicRoot, payload, options) {
for (const [key, value] of Object.entries(payload)) {
let subPayload = value;
let message = null;
// Special cases
if (key === 'color' && utils.objectHasProperties(subPayload, ['r', 'g', 'b'])) {
subPayload = [subPayload.r, subPayload.g, subPayload.b];
}
// Check Array first, since it is also an Object
if (subPayload === null || subPayload === undefined) {
message = '';
} else if (Array.isArray(subPayload)) {
message = subPayload.map((x) => `${x}`).join(',');
} else if (typeof subPayload === 'object') {
this.iteratePayloadAttributeOutput(`${topicRoot}${key}-`, subPayload, options);
} else {
message = typeof subPayload === 'string' ? subPayload : stringify(subPayload);
}
if (message !== null) {
await this.mqtt.publish(`${topicRoot}${key}`, message, options);
}
}
}
async callExtensionMethod(method, parameters, extensions=null) {
const result = {};
for (const extension of extensions || this.extensions) {
if (extension[method]) {
try {
result[extension.constructor.name] = await extension[method](...parameters);
} catch (error) {
/* istanbul ignore next */
logger.error(`Failed to call '${extension.constructor.name}' '${method}' (${error.stack})`);
/* istanbul ignore next */
if (process.env.JEST_WORKER_ID !== undefined) {
throw error;
}
}
}
}
return result;
}
}
module.exports = Controller;
+328
View File
@@ -0,0 +1,328 @@
import MQTT from './mqtt';
import Zigbee from './zigbee';
import EventBus from './eventBus';
import State from './state';
import logger from './util/logger';
import * as settings from './util/settings';
import utils from './util/utils';
import stringify from 'json-stable-stringify-without-jsonify';
import assert from 'assert';
import bind from 'bind-decorator';
// Extensions
import ExtensionFrontend from './extension/frontend';
import ExtensionPublish from './extension/publish';
import ExtensionReceive from './extension/receive';
import ExtensionNetworkMap from './extension/networkMap';
import ExtensionSoftReset from './extension/legacy/softReset';
import ExtensionHomeAssistant from './extension/homeassistant';
import ExtensionConfigure from './extension/configure';
import ExtensionDeviceGroupMembership from './extension/legacy/deviceGroupMembership';
import ExtensionBridgeLegacy from './extension/legacy/bridgeLegacy';
import ExtensionBridge from './extension/bridge';
import ExtensionGroups from './extension/groups';
import ExtensionAvailability from './extension/availability';
import ExtensionBind from './extension/bind';
import ExtensionReport from './extension/legacy/report';
import ExtensionOnEvent from './extension/onEvent';
import ExtensionOTAUpdate from './extension/otaUpdate';
import ExtensionExternalConverters from './extension/externalConverters';
import ExtensionExternalExtension from './extension/externalExtension';
const AllExtensions = [
ExtensionPublish, ExtensionReceive, ExtensionNetworkMap, ExtensionSoftReset, ExtensionHomeAssistant,
ExtensionConfigure, ExtensionDeviceGroupMembership, ExtensionBridgeLegacy, ExtensionBridge, ExtensionGroups,
ExtensionBind, ExtensionReport, ExtensionOnEvent, ExtensionOTAUpdate,
ExtensionExternalConverters, ExtensionFrontend, ExtensionExternalExtension, ExtensionAvailability,
];
type ExtensionArgs = [Zigbee, MQTT, State, PublishEntityState, EventBus,
(enable: boolean, name: string) => Promise<void>, () => void, (extension: Extension) => Promise<void>];
export class Controller {
private eventBus: EventBus;
private zigbee: Zigbee;
private state: State;
private mqtt: MQTT;
private restartCallback: () => void;
private exitCallback: (code: number, restart: boolean) => void;
private extensions: Extension[];
private extensionArgs: ExtensionArgs;
constructor(restartCallback: () => void, exitCallback: (code: number, restart: boolean) => void) {
logger.init();
this.eventBus = new EventBus( /* istanbul ignore next */ (error) => {
logger.error(`Error: ${error.message}`);
logger.debug(error.stack);
});
this.zigbee = new Zigbee(this.eventBus);
this.mqtt = new MQTT(this.eventBus);
this.state = new State(this.eventBus, this.zigbee);
this.restartCallback = restartCallback;
this.exitCallback = exitCallback;
// Initialize extensions.
this.extensionArgs = [this.zigbee, this.mqtt, this.state, this.publishEntityState, this.eventBus,
this.enableDisableExtension, this.restartCallback, this.addExtension];
this.extensions = [
new ExtensionBridge(...this.extensionArgs),
new ExtensionPublish(...this.extensionArgs),
new ExtensionReceive(...this.extensionArgs),
new ExtensionDeviceGroupMembership(...this.extensionArgs),
new ExtensionConfigure(...this.extensionArgs),
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),
new ExtensionAvailability(...this.extensionArgs),
settings.get().frontend && new ExtensionFrontend(...this.extensionArgs),
settings.get().advanced.legacy_api && new ExtensionBridgeLegacy(...this.extensionArgs),
settings.get().external_converters.length && new ExtensionExternalConverters(...this.extensionArgs),
settings.get().homeassistant && new ExtensionHomeAssistant(...this.extensionArgs),
/* istanbul ignore next */
settings.get().advanced.soft_reset_timeout !== 0 && new ExtensionSoftReset(...this.extensionArgs),
].filter((n) => n);
}
async start(): Promise<void> {
this.state.start();
logger.logOutput();
const info = await utils.getZigbee2MQTTVersion();
logger.info(`Starting Zigbee2MQTT version ${info.version} (commit #${info.commitHash})`);
// Start zigbee
let startResult;
try {
startResult = await this.zigbee.start();
this.eventBus.onAdapterDisconnected(this, this.onZigbeeAdapterDisconnected);
} catch (error) {
logger.error('Failed to start zigbee');
logger.error('Check https://www.zigbee2mqtt.io/guide/installation/20_zigbee2mqtt-fails-to-start.html for possible solutions'); /* eslint-disable-line max-len */
logger.error('Exiting...');
logger.error(error.stack);
await this.exit(1);
}
// Disable some legacy options on new network creation
if (startResult === 'reset') {
settings.set(['advanced', 'homeassistant_legacy_entity_attributes'], false);
settings.set(['advanced', 'legacy_api'], false);
settings.set(['advanced', 'legacy_availability_payload'], false);
settings.set(['device_options', 'legacy'], false);
this.enableDisableExtension(false, 'BridgeLegacy');
}
// Log zigbee clients on startup
const devices = this.zigbee.devices(false);
logger.info(`Currently ${devices.length} devices are joined:`);
for (const device of devices) {
const model = device.definition ?
`${device.definition.model} - ${device.definition.vendor} ${device.definition.description}` :
'Not supported';
logger.info(`${device.name} (${device.ieeeAddr}): ${model} (${device.zh.type})`);
}
// 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.');
}
await this.zigbee.permitJoin(settings.get().permit_join);
} catch (error) {
logger.error(`Failed to set permit join to ${settings.get().permit_join}`);
}
// MQTT
try {
await this.mqtt.connect();
} catch (error) {
logger.error(`MQTT failed to connect, exiting...`);
await this.zigbee.stop();
await this.exit(1);
}
// Call extensions
await this.callExtensions('start', [...this.extensions]);
// Send all cached states.
if (settings.get().advanced.cache_state_send_on_startup && settings.get().advanced.cache_state) {
for (const entity of [...devices, ...this.zigbee.groups()]) {
if (this.state.exists(entity)) {
this.publishEntityState(entity, this.state.get(entity), 'publishCached');
}
}
}
this.eventBus.onLastSeenChanged(this,
(data) => utils.publishLastSeen(data, settings.get(), false, this.publishEntityState));
logger.info(`Zigbee2MQTT started!`);
}
@bind async enableDisableExtension(enable: boolean, name: string): Promise<void> {
if (!enable) {
const extension = this.extensions.find((e) => e.constructor.name === name);
if (extension) {
await this.callExtensions('stop', [extension]);
this.extensions.splice(this.extensions.indexOf(extension), 1);
}
} else {
const Extension = AllExtensions.find((e) => e.name === name);
assert(Extension, `Extension '${name}' does not exist`);
const extension = new Extension(...this.extensionArgs);
this.extensions.push(extension);
await this.callExtensions('start', [extension]);
}
}
@bind async addExtension(extension: Extension): Promise<void> {
this.extensions.push(extension);
await this.callExtensions('start', [extension]);
}
async stop(restart = false): Promise<void> {
// Call extensions
await this.callExtensions('stop', this.extensions);
this.eventBus.removeListeners(this);
// Wrap-up
this.state.stop();
await this.mqtt.disconnect();
try {
await this.zigbee.stop();
logger.info('Stopped Zigbee2MQTT');
await this.exit(0, restart);
} catch (error) {
logger.error('Failed to stop Zigbee2MQTT');
await this.exit(1, restart);
}
}
async exit(code: number, restart = false): Promise<void> {
await logger.end();
this.exitCallback(code, restart);
}
@bind async onZigbeeAdapterDisconnected(): Promise<void> {
logger.error('Adapter disconnected, stopping');
await this.stop();
}
@bind async publishEntityState(entity: Group | Device, payload: KeyValue,
stateChangeReason?: StateChangeReason): Promise<void> {
let message = {...payload};
// Update state cache with new state.
const newState = this.state.set(entity, payload, stateChangeReason);
if (settings.get().advanced.cache_state) {
// Add cached state to payload
message = newState;
}
const options: MQTTOptions = {
retain: utils.getObjectProperty(entity.options, 'retain', false) as boolean,
qos: utils.getObjectProperty(entity.options, 'qos', 0) as 0 | 1 | 2,
};
const retention = utils.getObjectProperty(entity.options, 'retention', false);
if (retention !== false) {
options.properties = {messageExpiryInterval: retention as number};
}
if (entity.isDevice() && settings.get().mqtt.include_device_information) {
message.device = {
friendlyName: entity.name, model: entity.definition ? entity.definition.model : 'unknown',
ieeeAddr: entity.ieeeAddr, networkAddress: entity.zh.networkAddress, type: entity.zh.type,
manufacturerID: entity.zh.manufacturerID,
powerSource: entity.zh.powerSource, applicationVersion: entity.zh.applicationVersion,
stackVersion: entity.zh.stackVersion, zclVersion: entity.zh.zclVersion,
hardwareVersion: entity.zh.hardwareVersion, dateCode: entity.zh.dateCode,
softwareBuildID: entity.zh.softwareBuildID,
// Manufacturer name can contain \u0000, remove this.
// https://github.com/home-assistant/core/issues/85691
manufacturerName: entity.zh.manufacturerName?.split('\u0000')[0],
};
}
// Add lastseen
const lastSeen = settings.get().advanced.last_seen;
if (entity.isDevice() && lastSeen !== 'disable' && entity.zh.lastSeen) {
message.last_seen = utils.formatDate(entity.zh.lastSeen, lastSeen);
}
// Add device linkquality.
if (entity.isDevice() && entity.zh.linkquality !== undefined) {
message.linkquality = entity.zh.linkquality;
}
for (const extension of this.extensions) {
extension.adjustMessageBeforePublish?.(entity, message);
}
// Filter mqtt message attributes
utils.filterProperties(entity.options.filtered_attributes, message);
if (Object.entries(message).length) {
const output = settings.get().advanced.output;
if (output === 'attribute_and_json' || output === 'json') {
await this.mqtt.publish(entity.name, stringify(message), options);
}
if (output === 'attribute_and_json' || output === 'attribute') {
await this.iteratePayloadAttributeOutput(`${entity.name}/`, message, options);
}
}
this.eventBus.emitPublishEntityState({entity, message, stateChangeReason, payload});
}
async iteratePayloadAttributeOutput(topicRoot: string, payload: KeyValue, options: MQTTOptions): Promise<void> {
for (const [key, value] of Object.entries(payload)) {
let subPayload = value;
let message = null;
// Special cases
if (key === 'color' && utils.objectHasProperties(subPayload, ['r', 'g', 'b'])) {
subPayload = [subPayload.r, subPayload.g, subPayload.b];
}
// Check Array first, since it is also an Object
if (subPayload === null || subPayload === undefined) {
message = '';
} else if (Array.isArray(subPayload)) {
message = subPayload.map((x) => `${x}`).join(',');
} else if (typeof subPayload === 'object') {
await this.iteratePayloadAttributeOutput(`${topicRoot}${key}-`, subPayload, options);
} else {
message = typeof subPayload === 'string' ? subPayload : stringify(subPayload);
}
if (message !== null) {
await this.mqtt.publish(`${topicRoot}${key}`, message, options);
}
}
}
private async callExtensions(method: 'start' | 'stop', extensions: Extension[]): Promise<void> {
for (const extension of extensions) {
try {
await extension[method]?.();
} catch (error) {
/* istanbul ignore next */
logger.error(`Failed to call '${extension.constructor.name}' '${method}' (${error.stack})`);
}
}
}
}
module.exports = Controller;
+143 -108
View File
@@ -1,139 +1,174 @@
/* eslint-disable brace-style */
import events from 'events';
import * as ZHEvents from 'zigbee-herdsman/dist/controller/events';
import ExtensionTS from './extension/extensionts';
events.captureRejections = true;
declare global {
interface EventDeviceRenamed { device: Device, homeAssisantRename: boolean, from: string, to: string }
// TODO: remove resolved entity, replace by Device
interface EventDeviceRemoved { resolvedEntity: ResolvedEntity}
type EventMQTTMessage = { topic: string, message: string };
type EventMQTTMessagePublished = { topic: string, payload: string, options: {retain: boolean, qos: number} };
type EventPermitJoinChanged = ZHEvents.PermitJoinChangedPayload;
type EventLastSeenChanged = { device: Device };
type EventDeviceNetworkAddressChanged = { device: Device };
type EventDeviceAnnounce = { device: Device };
type EventDeviceInterview = { device: Device, status: 'started' | 'successful' | 'failed' };
type EventDeviceJoined = { device: Device };
type EventReportingDisabled = { device: ZHDevice }; // TODO zhdevice -> device
type EventDeviceLeave = { ieeeAddr: string };
// TODO: remove resolved entity, replace by Group
type EventGroupMembersChanged = { group: ResolvedEntity }; // TODO fill
type EventPublishEntityState = {
// TODO: remove resolved entity, replace by Device | Group and remove ieeeAddr
messagePayload: KeyValue, entity: ResolvedEntity, stateChangeReason: 'publishDebounce', payload: KeyValue,
ieeeAddr: string,
};
type EventDeviceMessage = {
type: ZHEvents.MessagePayloadType;
device: Device;
endpoint: ZHEndpoint;
linkquality: number;
groupID: number;
cluster: string | number;
data: KeyValue | Array<string | number>;
meta: {
zclTransactionSequenceNumber?: number;
};
};
}
type ListenerKey = string | ExtensionTS;
// eslint-disable-next-line
type ListenerKey = object;
export default class EventBus {
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
private callbacksByExtension: { [s: string]: { event: string, callback: (...args: any[]) => void }[] } = {};
private callbacksByExtension: { [s: string]: { event: string, callback: (...args: unknown[]) => void }[] } = {};
private emitter = new events.EventEmitter();
public emitAdapterDisconnected(): void {this.emitter.emit('adapterDisconnected');}
constructor(onError: (error: Error) => void) {
this.emitter.setMaxListeners(100);
this.emitter.on('error', onError);
}
public emitAdapterDisconnected(): void {
this.emitter.emit('adapterDisconnected');
}
public onAdapterDisconnected(key: ListenerKey, callback: () => void): void {
this.on('adapterDisconnected', callback, key);}
this.on('adapterDisconnected', callback, key);
}
public emitPermitJoinChanged(data: EventPermitJoinChanged): void {this.emitter.emit('permitJoinChanged', data);}
public onPermitJoinChanged(key: ListenerKey, callback: (data: EventPermitJoinChanged) => void): void {
this.on('permitJoinChanged', callback, key);}
public emitPermitJoinChanged(data: eventdata.PermitJoinChanged): void {
this.emitter.emit('permitJoinChanged', data);
}
public onPermitJoinChanged(key: ListenerKey, callback: (data: eventdata.PermitJoinChanged) => void): void {
this.on('permitJoinChanged', callback, key);
}
// public emitDeviceRenamed(data: EventDeviceRenamed): void {this.emitter.emit('deviceRenamed', data);}
public onDeviceRenamed(key: ListenerKey, callback: (data: EventDeviceRenamed) => void): void {
this.on('deviceRenamed', callback, key);}
public emitPublishAvailability(): void {
this.emitter.emit('publishAvailability');
}
public onPublishAvailability(key: ListenerKey, callback: () => void): void {
this.on('publishAvailability', callback, key);
}
// public emitDeviceRemoved(data: EventDeviceRemoved): void {this.emitter.emit('deviceRemoved', data);}
public onDeviceRemoved(key: ListenerKey, callback: (data: EventDeviceRemoved) => void): void {
this.on('deviceRemoved', callback, key);}
public emitEntityRenamed(data: eventdata.EntityRenamed): void {
this.emitter.emit('deviceRenamed', data);
}
public onEntityRenamed(key: ListenerKey, callback: (data: eventdata.EntityRenamed) => void): void {
this.on('deviceRenamed', callback, key);
}
public emitLastSeenChanged(data: EventLastSeenChanged): void {this.emitter.emit('lastSeenChanged', data);}
public onLastSeenChanged(key: ListenerKey, callback: (data: EventLastSeenChanged) => void): void {
this.on('lastSeenChanged', callback, key);}
public emitDeviceRemoved(data: eventdata.DeviceRemoved): void {
this.emitter.emit('deviceRemoved', data);
}
public onDeviceRemoved(key: ListenerKey, callback: (data: eventdata.DeviceRemoved) => void): void {
this.on('deviceRemoved', callback, key);
}
public emitDeviceNetworkAddressChanged(data: EventDeviceNetworkAddressChanged): void {
this.emitter.emit('deviceNetworkAddressChanged', data);}
public emitLastSeenChanged(data: eventdata.LastSeenChanged): void {
this.emitter.emit('lastSeenChanged', data);
}
public onLastSeenChanged(key: ListenerKey, callback: (data: eventdata.LastSeenChanged) => void): void {
this.on('lastSeenChanged', callback, key);
}
public emitDeviceNetworkAddressChanged(data: eventdata.DeviceNetworkAddressChanged): void {
this.emitter.emit('deviceNetworkAddressChanged', data);
}
public onDeviceNetworkAddressChanged(
key: ListenerKey, callback: (data: EventDeviceNetworkAddressChanged) => void): void {
this.on('deviceNetworkAddressChanged', callback, key);}
key: ListenerKey, callback: (data: eventdata.DeviceNetworkAddressChanged) => void): void {
this.on('deviceNetworkAddressChanged', callback, key);
}
public emitDeviceAnnounce(data: EventDeviceAnnounce): void {this.emitter.emit('deviceAnnounce', data);}
public onDeviceAnnounce(key: ListenerKey, callback: (data: EventDeviceAnnounce) => void): void {
this.on('deviceAnnounce', callback, key);}
public emitDeviceAnnounce(data: eventdata.DeviceAnnounce): void {
this.emitter.emit('deviceAnnounce', data);
}
public onDeviceAnnounce(key: ListenerKey, callback: (data: eventdata.DeviceAnnounce) => void): void {
this.on('deviceAnnounce', callback, key);
}
public emitDeviceInterview(data: EventDeviceInterview): void {this.emitter.emit('deviceInterview', data);}
public onDeviceInterview(key: ListenerKey, callback: (data: EventDeviceInterview) => void): void {
this.on('deviceInterview', callback, key);}
public emitDeviceInterview(data: eventdata.DeviceInterview): void {
this.emitter.emit('deviceInterview', data);
}
public onDeviceInterview(key: ListenerKey, callback: (data: eventdata.DeviceInterview) => void): void {
this.on('deviceInterview', callback, key);
}
public emitDeviceJoined(data: EventDeviceJoined): void {this.emitter.emit('deviceJoined', data);}
public onDeviceJoined(key: ListenerKey, callback: (data: EventDeviceJoined) => void): void {
this.on('deviceJoined', callback, key);}
public emitDeviceJoined(data: eventdata.DeviceJoined): void {
this.emitter.emit('deviceJoined', data);
}
public onDeviceJoined(key: ListenerKey, callback: (data: eventdata.DeviceJoined) => void): void {
this.on('deviceJoined', callback, key);
}
public emitDeviceLeave(data: EventDeviceLeave): void {this.emitter.emit('deviceLeave', data);}
public onDeviceLeave(key: ListenerKey, callback: (data: EventDeviceLeave) => void): void {
this.on('deviceLeave', callback, key);}
public emitEntityOptionsChanged(data: eventdata.EntityOptionsChanged): void {
this.emitter.emit('entityOptionsChanged', data);
}
public onEntityOptionsChanged(key: ListenerKey, callback: (data: eventdata.EntityOptionsChanged) => void): void {
this.on('entityOptionsChanged', callback, key);
}
public emitDeviceMessage(data: EventDeviceMessage): void {this.emitter.emit('deviceMessage', data);}
public onDeviceMessage(key: ListenerKey, callback: (data: EventDeviceMessage) => void): void {
this.on('deviceMessage', callback, key);}
public emitDeviceLeave(data: eventdata.DeviceLeave): void {
this.emitter.emit('deviceLeave', data);
}
public onDeviceLeave(key: ListenerKey, callback: (data: eventdata.DeviceLeave) => void): void {
this.on('deviceLeave', callback, key);
}
public emitMQTTMessage(data: EventMQTTMessage): void {this.emitter.emit('mqttMessage', data);}
public onMQTTMessage(key: ListenerKey, callback: (data: EventMQTTMessage) => void): void {
this.on('mqttMessage', callback, key);}
public emitDeviceMessage(data: eventdata.DeviceMessage): void {
this.emitter.emit('deviceMessage', data);
}
public onDeviceMessage(key: ListenerKey, callback: (data: eventdata.DeviceMessage) => void): void {
this.on('deviceMessage', callback, key);
}
public emitMQTTMessagePublished(data: EventMQTTMessagePublished): void {
this.emitter.emit('mqttMessagePublished', data);}
public onMQTTMessagePublished(key: ListenerKey, callback: (data: EventMQTTMessagePublished) => void): void {
this.on('mqttMessagePublished', callback, key);}
public emitMQTTMessage(data: eventdata.MQTTMessage): void {
this.emitter.emit('mqttMessage', data);
}
public onMQTTMessage(key: ListenerKey, callback: (data: eventdata.MQTTMessage) => void): void {
this.on('mqttMessage', callback, key);
}
// public emitPublishEntityState(data: EventPublishEntityState): void {
// this.emitter.emit('publishEntityState', data);}
public onPublishEntityState(key: ListenerKey, callback: (data: EventPublishEntityState) => void): void {
this.on('publishEntityState', callback, key);}
public emitMQTTMessagePublished(data: eventdata.MQTTMessagePublished): void {
this.emitter.emit('mqttMessagePublished', data);
}
public onMQTTMessagePublished(key: ListenerKey, callback: (data: eventdata.MQTTMessagePublished) => void): void {
this.on('mqttMessagePublished', callback, key);
}
// public emitGroupMembersChanged(data: EventGroupMembersChanged): void {
// this.emitter.emit('groupMembersChanged', data);}
public onGroupMembersChanged(key: ListenerKey, callback: (data: EventGroupMembersChanged) => void): void {
this.on('groupMembersChanged', callback, key);}
public emitPublishEntityState(data: eventdata.PublishEntityState): void {
this.emitter.emit('publishEntityState', data);
}
public onPublishEntityState(key: ListenerKey, callback: (data: eventdata.PublishEntityState) => void): void {
this.on('publishEntityState', callback, key);
}
public emitDevicesChanged(): void {this.emitter.emit('devicesChanged');}
public onDevicesChanged(key: ListenerKey, callback: () => void): void {this.on('devicesChanged', callback, key);}
public emitGroupMembersChanged(data: eventdata.GroupMembersChanged): void {
this.emitter.emit('groupMembersChanged', data);
}
public onGroupMembersChanged(key: ListenerKey, callback: (data: eventdata.GroupMembersChanged) => void): void {
this.on('groupMembersChanged', callback, key);
}
// public emitReportingDisabled(data: EventReportingDisabled): void {
// this.emitter.emit('reportingDisabled', data);}
public onReportingDisabled(key: ListenerKey, callback: (data: EventReportingDisabled) => void): void {
this.on('reportingDisabled', callback, key);}
public emitDevicesChanged(): void {
this.emitter.emit('devicesChanged');
}
public onDevicesChanged(key: ListenerKey, callback: () => void): void {
this.on('devicesChanged', callback, key);
}
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
private on(event: string, callback: (...args: any[]) => void, key: ListenerKey): void {
key = typeof key === 'string' ? key : key.constructor.name;
if (!this.callbacksByExtension[key]) this.callbacksByExtension[key] = [];
this.callbacksByExtension[key].push({event, callback});
public emitScenesChanged(): void {
this.emitter.emit('scenesChanged');
}
public onScenesChanged(key: ListenerKey, callback: () => void): void {
this.on('scenesChanged', callback, key);
}
public emitReconfigure(data: eventdata.Reconfigure): void {
this.emitter.emit('reconfigure', data);
}
public onReconfigure(key: ListenerKey, callback: (data: eventdata.Reconfigure) => void): void {
this.on('reconfigure', callback, key);
}
public emitStateChange(data: eventdata.StateChange): void {
this.emitter.emit('stateChange', data);
}
public onStateChange(key: ListenerKey, callback: (data: eventdata.StateChange) => void): void {
this.on('stateChange', callback, key);
}
private on(event: string, callback: (...args: unknown[]) => void, key: ListenerKey): void {
if (!this.callbacksByExtension[key.constructor.name]) this.callbacksByExtension[key.constructor.name] = [];
this.callbacksByExtension[key.constructor.name].push({event, callback});
this.emitter.on(event, callback);
}
// TODO: remove
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
emit(event: string, ...args: any[]): void {
this.emitter.emit(event, ...args);
}
public removeListeners(key: ListenerKey): void {
key = typeof key === 'string' ? key : key.constructor.name;
this.callbacksByExtension[key]?.forEach((e) => this.emitter.removeListener(e.event, e.callback));
this.callbacksByExtension[key.constructor.name]?.forEach(
(e) => this.emitter.removeListener(e.event, e.callback));
}
}
+94 -74
View File
@@ -1,14 +1,17 @@
import ExtensionTS from './extensionts';
import Extension from './extension';
import logger from '../util/logger';
import {sleep, hours, minutes, seconds, isAvailabilityEnabledForDevice} from '../util/utils';
import utils from '../util/utils';
import * as settings from '../util/settings';
import debounce from 'debounce';
import bind from 'bind-decorator';
// TODO
// - Enable for HA addon
// - Add to setting schema (when old availability is removed)
export default class Availability extends ExtensionTS {
const retrieveOnReconnect = [
{keys: ['state']},
{keys: ['brightness'], condition: (state: KeyValue): boolean => state.state === 'ON'},
{keys: ['color', 'color_temp'], condition: (state: KeyValue): boolean => state.state === 'ON'},
];
export default class Availability extends Extension {
private timers: {[s: string]: NodeJS.Timeout} = {};
private availabilityCache: {[s: string]: boolean} = {};
private retrieveStateDebouncers: {[s: string]: () => void} = {};
@@ -16,40 +19,42 @@ export default class Availability extends ExtensionTS {
private pingQueueExecuting = false;
private getTimeout(device: Device): number {
if (typeof device.settings.availability === 'object' && device.settings.availability?.timeout != null) {
return minutes(device.settings.availability.timeout);
if (typeof device.options.availability === 'object' && device.options.availability?.timeout != null) {
return utils.minutes(device.options.availability.timeout);
}
const key = this.isActiveDevice(device) ? 'active' : 'passive';
const availabilitySettings = settings.get().availability;
if (typeof availabilitySettings === 'object' && availabilitySettings[key]?.timeout != null) {
return minutes(availabilitySettings[key]?.timeout);
}
return key === 'active' ? minutes(10) : hours(25);
let value = settings.get().availability?.[key]?.timeout;
if (value == null) value = key == 'active' ? 10 : 1500;
return utils.minutes(value);
}
private isActiveDevice(device: Device): boolean {
return (device.type === 'Router' && device.powerSource !== 'Battery') ||
device.powerSource === 'Mains (single phase)';
return (device.zh.type === 'Router' && device.zh.powerSource !== 'Battery') ||
device.zh.powerSource === 'Mains (single phase)';
}
private isAvailable(device: Device): boolean {
const ago = Date.now() - device.lastSeen;
return ago < this.getTimeout(device);
private isAvailable(entity: Device | Group): boolean {
if (entity.isDevice()) {
const ago = Date.now() - entity.zh.lastSeen;
return ago < this.getTimeout(entity);
} else {
return entity.membersDevices().length === 0 ||
entity.membersDevices().map((d) => this.availabilityCache[d.ieeeAddr]).includes(true);
}
}
private resetTimer(device: Device): void {
clearTimeout(this.timers[device.ieeeAddr]);
// If the timer triggers, the device is not avaiable anymore otherwise resetTimer already have been called
// If the timer triggers, the device is not available anymore otherwise resetTimer already have been called
if (this.isActiveDevice(device)) {
// If device did not check in, ping it, if that fails it will be marked as offline
this.timers[device.ieeeAddr] = setTimeout(
() => this.addToPingQueue(device), this.getTimeout(device) + seconds(1));
() => this.addToPingQueue(device), this.getTimeout(device) + utils.seconds(1));
} else {
this.timers[device.ieeeAddr] = setTimeout(
() => this.publishAvailability(device, true), this.getTimeout(device) + seconds(1));
() => this.publishAvailability(device, true), this.getTimeout(device) + utils.seconds(1));
}
}
@@ -75,15 +80,15 @@ export default class Availability extends ExtensionTS {
try {
// Enable recovery if device is marked as available and first ping fails.
const disableRecovery = !(i == 1 && available);
await device.ping(disableRecovery);
await device.zh.ping(disableRecovery);
pingedSuccessfully = true;
logger.debug(`Succesfully pinged '${device.name}' (attempt ${i + 1}/${attempts})`);
logger.debug(`Successfully pinged '${device.name}' (attempt ${i + 1}/${attempts})`);
break;
} catch (error) {
logger.error(`Failed to ping '${device.name}' (attempt ${i + 1}/${attempts}, ${error.message})`);
logger.warn(`Failed to ping '${device.name}' (attempt ${i + 1}/${attempts}, ${error.message})`);
// Try again in 3 seconds.
const lastAttempt = i - 1 === attempts;
!lastAttempt && await sleep(3);
!lastAttempt && await utils.sleep(3);
}
}
@@ -92,66 +97,84 @@ export default class Availability extends ExtensionTS {
this.removeFromPingQueue(device);
// Sleep 2 seconds before executing next ping
await sleep(2);
await utils.sleep(2);
this.pingQueueExecuting = false;
this.pingQueueExecuteNext();
}
override async start(): Promise<void> {
logger.warn('Using experimental new availability feature');
this.eventBus.onEntityRenamed(this, (data) => {
if (utils.isAvailabilityEnabledForEntity(data.entity, settings.get())) {
this.mqtt.publish(`${data.from}/availability`, null, {retain: true, qos: 0});
this.publishAvailability(data.entity, false, true);
}
});
this.eventBus.onDeviceRenamed(this, (data: EventDeviceRenamed) =>
this.publishAvailability(data.device, false, true));
this.eventBus.onDeviceLeave(this, (data: EventDeviceLeave) => clearTimeout(this.timers[data.ieeeAddr]));
this.eventBus.onDeviceAnnounce(this, (data: EventDeviceAnnounce) => this.retrieveState(data.device));
this.eventBus.onDeviceRemoved(this, (data) => clearTimeout(this.timers[data.ieeeAddr]));
this.eventBus.onDeviceLeave(this, (data) => clearTimeout(this.timers[data.ieeeAddr]));
this.eventBus.onDeviceAnnounce(this, (data) => this.retrieveState(data.device));
this.eventBus.onLastSeenChanged(this, this.onLastSeenChanged);
this.eventBus.onPublishAvailability(this, this.publishAvailabilityForAllEntities);
this.eventBus.onGroupMembersChanged(this, (data) => this.publishAvailability(data.group, false));
this.publishAvailabilityForAllEntities();
}
for (const device of this.zigbee.getClients()) {
if (isAvailabilityEnabledForDevice(device, settings.get())) {
@bind private publishAvailabilityForAllEntities(): void {
for (const entity of [...this.zigbee.devices(false), ...this.zigbee.groups()]) {
if (utils.isAvailabilityEnabledForEntity(entity, settings.get())) {
// Publish initial availablility
this.publishAvailability(device, true);
this.publishAvailability(entity, true, false, true);
this.resetTimer(device);
if (entity.isDevice()) {
this.resetTimer(entity);
// If an active device is initially unavailable, ping it.
if (this.isActiveDevice(device) && !this.isAvailable(device)) {
this.addToPingQueue(device);
// If an active device is initially unavailable, ping it.
if (this.isActiveDevice(entity) && !this.isAvailable(entity)) {
this.addToPingQueue(entity);
}
}
}
}
}
private publishAvailability(device: Device, logLastSeen: boolean, forcePublish=false): void {
if (logLastSeen) {
const ago = Date.now() - device.lastSeen;
if (this.isActiveDevice(device)) {
logger.debug(
`Active device '${device.name}' was last seen '${(ago / minutes(1)).toFixed(2)}' minutes ago.`);
private publishAvailability(entity: Device | Group, logLastSeen: boolean,
forcePublish=false, skipGroups=false): void {
if (logLastSeen && entity.isDevice()) {
const ago = Date.now() - entity.zh.lastSeen;
if (this.isActiveDevice(entity)) {
logger.debug(`Active device '${entity.name}' was last seen ` +
`'${(ago / utils.minutes(1)).toFixed(2)}' minutes ago.`);
} else {
logger.debug(
`Passive device '${device.name}' was last seen '${(ago / hours(1)).toFixed(2)}' hours ago.`);
`Passive device '${entity.name}' was last seen '${(ago / utils.hours(1)).toFixed(2)}' hours ago.`);
}
}
const available = this.isAvailable(device);
if (!forcePublish && this.availabilityCache[device.ieeeAddr] == available) {
const available = this.isAvailable(entity);
if (!forcePublish && this.availabilityCache[entity.ID] == available) {
return;
}
if (device.ieeeAddr in this.availabilityCache && available &&
this.availabilityCache[device.ieeeAddr] === false) {
logger.debug(`Device '${device.name}' reconnected`);
this.retrieveState(device);
if (entity.isDevice() && entity.ieeeAddr in this.availabilityCache && available &&
this.availabilityCache[entity.ieeeAddr] === false) {
logger.debug(`Device '${entity.name}' reconnected`);
this.retrieveState(entity);
}
const topic = `${device.name}/availability`;
const payload = available ? 'online' : 'offline';
this.availabilityCache[device.ieeeAddr] = available;
const topic = `${entity.name}/availability`;
const payload = utils.availabilityPayload(available ? 'online' : 'offline', settings.get());
this.availabilityCache[entity.ID] = available;
this.mqtt.publish(topic, payload, {retain: true, qos: 0});
if (!skipGroups && entity.isDevice()) {
this.zigbee.groups().filter((g) => g.hasMember(entity))
.filter((g) => utils.isAvailabilityEnabledForEntity(g, settings.get()))
.forEach((g) => this.publishAvailability(g, false, forcePublish));
}
}
@bind private onLastSeenChanged(data: EventLastSeenChanged): void {
if (isAvailabilityEnabledForDevice(data.device, settings.get())) {
@bind private onLastSeenChanged(data: eventdata.LastSeenChanged): void {
if (utils.isAvailabilityEnabledForEntity(data.device, settings.get())) {
// Remove from ping queue, not necessary anymore since we know the device is online.
this.removeFromPingQueue(data.device);
this.resetTimer(data.device);
@@ -160,8 +183,8 @@ export default class Availability extends ExtensionTS {
}
override async stop(): Promise<void> {
super.stop();
Object.values(this.timers).forEach((t) => clearTimeout(t));
super.stop();
}
private retrieveState(device: Device): void {
@@ -169,26 +192,23 @@ export default class Availability extends ExtensionTS {
* Retrieve state of a device in a debounced manner, this function is called on a 'deviceAnnounce' which a
* device can send multiple times after each other.
*/
if (device.definition && !device.interviewing && !this.retrieveStateDebouncers[device.ieeeAddr]) {
if (device.definition && !device.zh.interviewing && !this.retrieveStateDebouncers[device.ieeeAddr]) {
this.retrieveStateDebouncers[device.ieeeAddr] = debounce(async () => {
try {
logger.debug(`Retrieving state of '${device.name}' after reconnect`);
// Color and color temperature converters do both, only needs to be called once.
const keySet = [['state'], ['brightness'], ['color', 'color_temp']];
for (const keys of keySet) {
const converter = device.definition.toZigbee.find((c) => c.key.find((k) => keys.includes(k)));
await converter?.convertGet?.(device.endpoint(), keys[0],
{message: this.state.get(device.ieeeAddr) || {}, mapped: device.definition});
}
} catch (error) {
logger.error(`Failed to read state of '${device.name}' after reconnect (${error.message})`);
logger.debug(`Retrieving state of '${device.name}' after reconnect`);
// Color and color temperature converters do both, only needs to be called once.
for (const item of retrieveOnReconnect) {
if (item.condition && this.state.get(device) && !item.condition(this.state.get(device))) continue;
const converter = device.definition.toZigbee.find((c) => c.key.find((k) => item.keys.includes(k)));
await converter?.convertGet?.(device.endpoint(), item.keys[0],
{message: this.state.get(device), mapped: device.definition})
.catch((e) => {
logger.error(`Failed to read state of '${device.name}' after reconnect (${e.message})`);
});
await utils.sleep(500);
}
}, seconds(2));
}, utils.seconds(2));
}
this.retrieveStateDebouncers[device.ieeeAddr]?.();
}
}
// TODO_finished: : change class to export default
module.exports = Availability;
+188 -150
View File
@@ -1,36 +1,44 @@
const settings = require('../util/settings');
const logger = require('../util/logger');
const utils = require('../util/utils');
import * as settings from '../util/settings';
import logger from '../util/logger';
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 bind from 'bind-decorator';
import Device from '../model/device';
import Group from '../model/group';
const legacyApi = settings.get().advanced.legacy_api;
const legacyTopicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/(bind|unbind)/.+$`);
const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/request/device/(bind|unbind)`);
const Extension = require('./extension');
const stringify = require('json-stable-stringify-without-jsonify');
const debounce = require('debounce');
const zigbeeHersdman = require('zigbee-herdsman');
const clusterCandidates = ['genScenes', 'genOnOff', 'genLevelCtrl', 'lightingColorCtrl', 'closuresWindowCovering',
'hvacThermostat', 'msTemperatureMeasurement'];
'hvacThermostat', 'msIlluminanceMeasurement', 'msTemperatureMeasurement', 'msRelativeHumidity',
'msSoilMoisture', 'msCO2'];
// See zigbee-herdsman-converters
const defaultBindGroup = {type: 'group_number', ID: 901};
const defaultBindGroup = {type: 'group_number', ID: 901, name: 'default_bind_group'};
const defaultReportConfiguration = {
minimumReportInterval: 5, maximumReportInterval: 3600, reportableChange: 1,
};
const getColorCapabilities = async (endpoint) => {
const getColorCapabilities = async (endpoint: zh.Endpoint): Promise<{colorTemperature: boolean, colorXY: boolean}> => {
if (endpoint.getClusterAttributeValue('lightingColorCtrl', 'colorCapabilities') == null) {
await endpoint.read('lightingColorCtrl', ['colorCapabilities']);
}
const value = endpoint.getClusterAttributeValue('lightingColorCtrl', 'colorCapabilities');
const value = endpoint.getClusterAttributeValue('lightingColorCtrl', 'colorCapabilities') as number;
return {
colorTemperature: (value & 1<<4) > 0,
colorXY: (value & 1<<3) > 0,
};
};
const reportClusters = {
const reportClusters: {[s: string]:
{attribute: string, minimumReportInterval: number, maximumReportInterval: number, reportableChange: number
condition?: (endpoint: zh.Endpoint) => Promise<boolean>}[]} =
{
'genOnOff': [
{attribute: 'onOff', ...defaultReportConfiguration, minimumReportInterval: 0, reportableChange: 0},
],
@@ -40,15 +48,15 @@ const reportClusters = {
'lightingColorCtrl': [
{
attribute: 'colorTemperature', ...defaultReportConfiguration,
condition: async (endpoint) => (await getColorCapabilities(endpoint)).colorTemperature,
condition: async (endpoint): Promise<boolean> => (await getColorCapabilities(endpoint)).colorTemperature,
},
{
attribute: 'currentX', ...defaultReportConfiguration,
condition: async (endpoint) => (await getColorCapabilities(endpoint)).colorXY,
condition: async (endpoint): Promise<boolean> => (await getColorCapabilities(endpoint)).colorXY,
},
{
attribute: 'currentY', ...defaultReportConfiguration,
condition: async (endpoint) => (await getColorCapabilities(endpoint)).colorXY,
condition: async (endpoint): Promise<boolean> => (await getColorCapabilities(endpoint)).colorXY,
},
],
'closuresWindowCovering': [
@@ -57,7 +65,14 @@ const reportClusters = {
],
};
const pollOnMessage = [
type PollOnMessage = {
cluster: {[s: string]: {type: string, data: KeyValue}[]}
read: {cluster: string, attributes: string[], attributesForEndpoint?: (endpoint: zh.Endpoint) => Promise<string[]>}
manufacturerIDs: number[],
manufacturerNames: string [],
}[];
const pollOnMessage: PollOnMessage = [
{
// On messages that have the cluster and type of below
cluster: {
@@ -82,10 +97,16 @@ const pollOnMessage = [
read: {cluster: 'genLevelCtrl', attributes: ['currentLevel']},
// When the bound devices/members of group have the following manufacturerIDs
manufacturerIDs: [
zigbeeHersdman.Zcl.ManufacturerCode.Philips,
zigbeeHersdman.Zcl.ManufacturerCode.ATMEL,
zigbeeHersdman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
zigbeeHersdman.Zcl.ManufacturerCode.MUELLER_LICHT_INT,
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,
],
manufacturerNames: [
'GLEDOPTO',
'Trust International B.V.\u0000',
],
},
{
@@ -112,10 +133,16 @@ const pollOnMessage = [
},
read: {cluster: 'genOnOff', attributes: ['onOff']},
manufacturerIDs: [
zigbeeHersdman.Zcl.ManufacturerCode.Philips,
zigbeeHersdman.Zcl.ManufacturerCode.ATMEL,
zigbeeHersdman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
zigbeeHersdman.Zcl.ManufacturerCode.MUELLER_LICHT_INT,
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,
],
manufacturerNames: [
'GLEDOPTO',
'Trust International B.V.\u0000',
],
},
{
@@ -126,49 +153,60 @@ const pollOnMessage = [
},
read: {
cluster: 'lightingColorCtrl',
attributes: [],
attributes: [] as string[],
// Since not all devices support the same attributes they need to be calculated dynamically
// depending on the capabilities of the endpoint.
attributesForEndpoint: async (endpoint) => {
attributesForEndpoint: async (endpoint): Promise<string[]> => {
const supportedAttrs = await getColorCapabilities(endpoint);
const readAttrs = [];
const readAttrs: string[] = [];
supportedAttrs.colorXY && readAttrs.push('currentX', 'currentY');
supportedAttrs.colorTemperature && readAttrs.push('colorTemperature');
return readAttrs;
},
},
manufacturerIDs: [
zigbeeHersdman.Zcl.ManufacturerCode.Philips,
zigbeeHersdman.Zcl.ManufacturerCode.ATMEL,
zigbeeHersdman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
zigbeeHersdman.Zcl.ManufacturerCode.MUELLER_LICHT_INT,
zigbeeHerdsman.Zcl.ManufacturerCode.Philips,
zigbeeHerdsman.Zcl.ManufacturerCode.ATMEL,
zigbeeHerdsman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
zigbeeHerdsman.Zcl.ManufacturerCode.MUELLER_LICHT_INT,
zigbeeHerdsman.Zcl.ManufacturerCode.TELINK,
// Note: ManufacturerCode.BUSCH_JAEGER is left out intentionally here as their devices don't support colors
],
manufacturerNames: [
'GLEDOPTO',
'Trust International B.V.\u0000',
],
},
];
class Bind extends Extension {
constructor(zigbee, mqtt, state, publishEntityState, eventBus) {
super(zigbee, mqtt, state, publishEntityState, eventBus);
this.legacyApi = settings.get().advanced.legacy_api;
this.eventBus.on(`groupMembersChanged`, (d) => this.groupMembersChanged(d), this.constructor.name);
this.pollDebouncers = {};
interface ParsedMQTTMessage {
type: 'bind' | 'unbind', sourceKey: string, targetKey: string, clusters: string[], skipDisableReporting: boolean
}
export default class Bind extends Extension {
private pollDebouncers: {[s: string]: () => void} = {};
override async start(): Promise<void> {
this.eventBus.onDeviceMessage(this, this.poll);
this.eventBus.onMQTTMessage(this, this.onMQTTMessage);
this.eventBus.onGroupMembersChanged(this, this.onGroupMembersChanged);
}
parseMQTTMessage(topic, message) {
let type = null;
private parseMQTTMessage(data: eventdata.MQTTMessage): ParsedMQTTMessage {
let type: 'bind' | 'unbind' = null;
let sourceKey = null;
let targetKey = null;
let clusters = null;
let skipDisableReporting = false;
if (this.legacyApi && topic.match(legacyTopicRegex)) {
topic = topic.replace(`${settings.get().mqtt.base_topic}/bridge/`, '');
type = topic.split('/')[0];
if (legacyApi && data.topic.match(legacyTopicRegex)) {
const topic = data.topic.replace(`${settings.get().mqtt.base_topic}/bridge/`, '');
type = topic.split('/')[0] as 'bind' | 'unbind';
sourceKey = topic.replace(`${type}/`, '');
targetKey = message;
} else if (topic.match(topicRegex)) {
type = topic.endsWith('unbind') ? 'unbind' : 'bind';
message = JSON.parse(message);
targetKey = data.message;
} else if (data.topic.match(topicRegex)) {
type = data.topic.endsWith('unbind') ? 'unbind' : 'bind';
const message = JSON.parse(data.message);
sourceKey = message.from;
targetKey = message.to;
clusters = message.clusters;
@@ -178,32 +216,33 @@ class Bind extends Extension {
return {type, sourceKey, targetKey, clusters, skipDisableReporting};
}
async onMQTTMessage(topic, message) {
const {type, sourceKey, targetKey, clusters, skipDisableReporting} = this.parseMQTTMessage(topic, message);
@bind private async onMQTTMessage(data: eventdata.MQTTMessage): Promise<void> {
const {type, sourceKey, targetKey, clusters, skipDisableReporting} = this.parseMQTTMessage(data);
if (!type) return null;
message = utils.parseJSON(message, message);
const message = utils.parseJSON(data.message, data.message);
let error = null;
const source = this.zigbee.resolveEntityLegacy(sourceKey);
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.resolveEntityLegacy(targetKey);
const responseData = {from: sourceKey, to: targetKey};
defaultBindGroup : this.zigbee.resolveEntity(parsedTarget.ID);
const responseData: KeyValue = {from: sourceKey, to: targetKey};
if (!source || source.type !== 'device') {
if (!source || !(source instanceof Device)) {
error = `Source device '${sourceKey}' does not exist`;
} else if (!target) {
error = `Target device or group '${targetKey}' does not exist`;
} else {
const sourceName = source.settings.friendlyName;
const targetName = targetKey === 'default_bind_group' ? targetKey : target.settings.friendlyName;
const successfulClusters = [];
const successfulClusters: string[] = [];
const failedClusters = [];
const attemptedClusters = [];
let bindTarget = null;
if (target.type === 'group') bindTarget = target.group;
else if (target.type === 'group_number') bindTarget = target.ID;
else bindTarget = target.endpoint;
const bindSource: zh.Endpoint = source.endpoint(parsedSource.endpoint);
let bindTarget: number | zh.Group | zh.Endpoint = null;
if (target instanceof Device) bindTarget = target.endpoint(parsedTarget.endpoint);
else if (target instanceof Group) bindTarget = target.zh;
else bindTarget = Number(target.ID);
// Find which clusters are supported by both the source and target.
// Groups are assumed to support all clusters.
@@ -211,34 +250,34 @@ class Bind extends Extension {
if (clusters && !clusters.includes(cluster)) continue;
let matchingClusters = false;
const anyClusterValid = target.type === 'group' || target.type === 'group_number' ||
target.device.type === 'Coordinator';
const anyClusterValid = utils.isZHGroup(bindTarget) || typeof bindTarget === 'number' ||
(target as Device).zh.type === 'Coordinator';
if (!anyClusterValid) {
matchingClusters = ((target.endpoint.supportsInputCluster(cluster) &&
source.endpoint.supportsOutputCluster(cluster)) ||
(source.endpoint.supportsInputCluster(cluster) &&
target.endpoint.supportsOutputCluster(cluster)) );
if (!anyClusterValid && utils.isEndpoint(bindTarget)) {
matchingClusters = ((bindTarget.supportsInputCluster(cluster) &&
bindSource.supportsOutputCluster(cluster)) ||
(bindSource.supportsInputCluster(cluster) &&
bindTarget.supportsOutputCluster(cluster)) );
}
const sourceValid = source.endpoint.supportsInputCluster(cluster) ||
source.endpoint.supportsOutputCluster(cluster);
const sourceValid = bindSource.supportsInputCluster(cluster) ||
bindSource.supportsOutputCluster(cluster);
if ( sourceValid && (anyClusterValid || matchingClusters)) {
logger.debug(`${type}ing cluster '${cluster}' from '${sourceName}' to '${targetName}'`);
logger.debug(`${type}ing cluster '${cluster}' from '${source.name}' to '${target.name}'`);
attemptedClusters.push(cluster);
try {
if (type === 'bind') {
await source.endpoint.bind(cluster, bindTarget);
await bindSource.bind(cluster, bindTarget);
} else {
await source.endpoint.unbind(cluster, bindTarget);
await bindSource.unbind(cluster, bindTarget);
}
successfulClusters.push(cluster);
logger.info(
`Successfully ${type === 'bind' ? 'bound' : 'unbound'} cluster '${cluster}' from ` +
`'${sourceName}' to '${targetName}'`,
`'${source.name}' to '${target.name}'`,
);
/* istanbul ignore else */
@@ -246,14 +285,14 @@ class Bind extends Extension {
this.mqtt.publish(
'bridge/log',
stringify({type: `device_${type}`,
message: {from: sourceName, to: targetName, cluster}}),
message: {from: source.name, to: target.name, cluster}}),
);
}
} catch (error) {
failedClusters.push(cluster);
logger.error(
`Failed to ${type} cluster '${cluster}' from '${sourceName}' to ` +
`'${targetName}' (${error})`,
`Failed to ${type} cluster '${cluster}' from '${source.name}' to ` +
`'${target.name}' (${error})`,
);
/* istanbul ignore else */
@@ -261,7 +300,7 @@ class Bind extends Extension {
this.mqtt.publish(
'bridge/log',
stringify({type: `device_${type}_failed`,
message: {from: sourceName, to: targetName, cluster}}),
message: {from: source.name, to: target.name, cluster}}),
);
}
}
@@ -269,14 +308,14 @@ class Bind extends Extension {
}
if (attemptedClusters.length === 0) {
logger.error(`Nothing to ${type} from '${sourceName}' to '${targetName}'`);
logger.error(`Nothing to ${type} from '${source.name}' to '${target.name}'`);
error = `Nothing to ${type}`;
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
this.mqtt.publish(
'bridge/log',
stringify({type: `device_${type}_failed`, message: {from: sourceName, to: targetName}}),
stringify({type: `device_${type}_failed`, message: {from: source.name, to: target.name}}),
);
}
} else if (failedClusters.length === attemptedClusters.length) {
@@ -288,15 +327,15 @@ class Bind extends Extension {
if (successfulClusters.length !== 0) {
if (type === 'bind') {
await this.setupReporting(source.endpoint.binds.filter((b) =>
await this.setupReporting(bindSource.binds.filter((b) =>
successfulClusters.includes(b.cluster.name) && b.target === bindTarget));
} else if ((target.type !== 'group_number') && !skipDisableReporting) {
} else if ((typeof bindTarget !== 'number') && !skipDisableReporting) {
await this.disableUnnecessaryReportings(bindTarget);
}
}
}
const triggeredViaLegacyApi = topic.match(legacyTopicRegex);
const triggeredViaLegacyApi = data.topic.match(legacyTopicRegex);
if (!triggeredViaLegacyApi) {
const response = utils.getResponse(message, responseData, error);
await this.mqtt.publish(`bridge/response/device/${type}`, stringify(response));
@@ -305,15 +344,15 @@ class Bind extends Extension {
if (error) {
logger.error(error);
} else {
this.eventBus.emit(`devicesChanged`);
this.eventBus.emitDevicesChanged();
}
}
async groupMembersChanged(data) {
@bind async onGroupMembersChanged(data: eventdata.GroupMembersChanged): Promise<void> {
if (data.action === 'add') {
const bindsToGroup = this.zigbee.getClientsLegacy().map((c) => c.endpoints)
const bindsToGroup = this.zigbee.devices(false).map((c) => c.zh.endpoints)
.reduce((a, v) => a.concat(v)).map((e) => e.binds)
.reduce((a, v) => a.concat(v)).filter((b) => b.target === data.group.group);
.reduce((a, v) => a.concat(v)).filter((b) => b.target === data.group.zh);
await this.setupReporting(bindsToGroup);
} else { // action === remove/remove_all
if (!data.skipDisableReporting) {
@@ -322,8 +361,8 @@ class Bind extends Extension {
}
}
getSetupReportingEndpoints(bind, coordinatorEp) {
const endpoints = bind.target.constructor.name === 'Group' ? bind.target.members : [bind.target];
getSetupReportingEndpoints(bind: zh.Bind, coordinatorEp: zh.Endpoint): zh.Endpoint[] {
const endpoints = utils.isEndpoint(bind.target) ? [bind.target] : bind.target.members;
return endpoints.filter((e) => {
const supportsInputCluster = e.supportsInputCluster(bind.cluster.name);
const hasConfiguredReporting = !!e.configuredReportings.find((c) => c.cluster.name === bind.cluster.name);
@@ -332,79 +371,77 @@ class Bind extends Extension {
});
}
async setupReporting(binds) {
const coordinator = this.zigbee.getDevicesByTypeLegacy('Coordinator')[0];
const coordinatorEndpoint = coordinator.getEndpoint(1);
async setupReporting(binds: zh.Bind[]): Promise<void> {
const coordinatorEndpoint = this.zigbee.firstCoordinatorEndpoint();
for (const bind of binds.filter((b) => b.cluster.name in reportClusters)) {
for (const endpoint of this.getSetupReportingEndpoints(bind, coordinatorEndpoint)) {
const entity = `${this.zigbee.resolveEntityLegacy(endpoint.getDevice()).name}/${endpoint.ID}`;
const entity = `${this.zigbee.resolveEntity(endpoint.getDevice()).name}/${endpoint.ID}`;
try {
await endpoint.bind(bind.cluster.name, coordinatorEndpoint);
const items = [];
for (const c of reportClusters[bind.cluster.name]) {
/* istanbul ignore else */
if (!c.condition || await c.condition(endpoint)) {
const i = {...c};
delete i.condition;
items.push(i);
}
}
const items = await reportClusters[bind.cluster.name]
.filter(async (a) => !a.condition || await a.condition(endpoint))
.map((a) => {
const result = {...a};
delete result.condition;
return result;
});
await endpoint.configureReporting(bind.cluster.name, items);
logger.info(`Succesfully setup reporting for '${entity}' cluster '${bind.cluster.name}'`);
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}'`);
}
}
}
this.eventBus.emit(`devicesChanged`);
this.eventBus.emitDevicesChanged();
}
async disableUnnecessaryReportings(target) {
const coordinator = this.zigbee.getDevicesByTypeLegacy('Coordinator')[0];
const coordinatorEndpoint = coordinator.getEndpoint(1);
const endpoints = target.constructor.name === 'Group' ? target.members : [target];
async disableUnnecessaryReportings(target: zh.Group | zh.Endpoint): Promise<void> {
const coordinator = this.zigbee.firstCoordinatorEndpoint();
const endpoints = utils.isEndpoint(target) ? [target] : target.members;
for (const endpoint of endpoints) {
const entity = `${this.zigbee.resolveEntityLegacy(endpoint.getDevice()).name}/${endpoint.ID}`;
const boundClusters = endpoint.binds.filter((b) => b.target === coordinatorEndpoint)
const device = this.zigbee.resolveEntity(endpoint.getDevice()) as Device;
const entity = `${device.name}/${endpoint.ID}`;
const boundClusters = endpoint.binds.filter((b) => b.target === coordinator)
.map((b) => b.cluster.name);
const requiredClusters = this.zigbee.getClientsLegacy().map((c) => c.endpoints)
const requiredClusters = this.zigbee.devices(false).map((c) => c.zh.endpoints)
.reduce((a, v) => a.concat(v))
.map((e) => e.binds).reduce((a, v) => a.concat(v)).filter((bind) => {
if (bind.target.constructor.name === 'Group') {
return bind.target.members.includes(endpoint);
} else {
if (utils.isEndpoint(bind.target)) {
return bind.target === endpoint;
} else {
return bind.target.members.includes(endpoint);
}
}).map((b) => b.cluster.name).filter((v, i, a) => a.indexOf(v) === i);
for (const cluster of boundClusters.filter((c) => !requiredClusters.includes(c) && c in reportClusters)) {
try {
await endpoint.unbind(cluster, coordinatorEndpoint);
const items = await reportClusters[cluster]
.filter(async (a) => !a.condition || await a.condition(endpoint))
.map((a) => {
const result = {...a, maximumReportInterval: 0xFFFF};
delete result.condition;
return result;
});
await endpoint.unbind(cluster, coordinator);
const items = [];
for (const item of reportClusters[cluster]) {
/* istanbul ignore else */
if (!item.condition || await item.condition(endpoint)) {
const i = {...item};
delete i.condition;
items.push({...i, maximumReportInterval: 0xFFFF});
}
}
await endpoint.configureReporting(cluster, items);
logger.info(`Succesfully disabled reporting for '${entity}' cluster '${cluster}'`);
logger.info(`Successfully disabled reporting for '${entity}' cluster '${cluster}'`);
} catch (error) {
logger.warn(`Failed to disable reporting for '${entity}' cluster '${cluster}'`);
}
}
this.eventBus.emit('reportingDisabled', {device: endpoint.getDevice()});
this.eventBus.emitReconfigure({device});
}
}
async onZigbeeEvent(type, data, resolvedEntity) {
if (type === 'message') {
this.poll(data);
}
}
async poll(message) {
@bind async poll(data: eventdata.DeviceMessage): Promise<void> {
/**
* This method poll bound endpoints and group members for state changes.
*
@@ -414,31 +451,29 @@ class Bind extends Extension {
* When we receive a message from a Hue dimmer we read the brightness from the bulb (if bound).
*/
const polls = pollOnMessage.filter((p) =>
p.cluster[message.cluster] && p.cluster[message.cluster].find((c) => c.type === message.type &&
utils.equalsPartial(message.data, c.data)),
);
p.cluster[data.cluster]?.find((c) => c.type === data.type && utils.equalsPartial(data.data, c.data)));
if (polls.length) {
let toPoll = [];
const toPoll: Set<zh.Endpoint> = new Set();
// Add bound devices
toPoll = toPoll.concat([].concat(...message.device.endpoints.map((e) =>
e.binds.map((e) => e).filter((e) => e.target))));
toPoll = toPoll.filter((e) => e.target.constructor.name === 'Endpoint');
toPoll = toPoll.filter((e) => e.target.getDevice().type !== 'Coordinator');
toPoll = toPoll.map((e) => e.target);
// If message is published to a group, add members of the group
const group = message.groupID !== 0 ? this.zigbee.getGroupByIDLegacy(message.groupID) : null;
if (group) {
toPoll = toPoll.concat(group.members);
for (const endpoint of data.device.zh.endpoints) {
for (const bind of endpoint.binds) {
if (utils.isEndpoint(bind.target) && bind.target.getDevice().type !== 'Coordinator') {
toPoll.add(bind.target);
}
}
}
toPoll = new Set(toPoll);
// If message is published to a group, add members of the group
const group = data.groupID && data.groupID !== 0 && this.zigbee.groupByID(data.groupID);
if (group) {
group.zh.members.forEach((m) => toPoll.add(m));
}
for (const endpoint of toPoll) {
for (const poll of polls) {
if (!poll.manufacturerIDs.includes(endpoint.getDevice().manufacturerID) ||
if ((!poll.manufacturerIDs.includes(endpoint.getDevice().manufacturerID) &&
!poll.manufacturerNames.includes(endpoint.getDevice().manufacturerName)) ||
!endpoint.supportsInputCluster(poll.read.cluster)) {
continue;
}
@@ -449,10 +484,15 @@ class Bind extends Extension {
readAttrs = [...poll.read.attributes, ...attrsForEndpoint];
}
const key = `${endpoint.deviceIeeeAddress}_${endpoint.ID}_${pollOnMessage.indexOf(poll)}`;
const key = `${endpoint.getDevice().ieeeAddr}_${endpoint.ID}_${pollOnMessage.indexOf(poll)}`;
if (!this.pollDebouncers[key]) {
this.pollDebouncers[key] = debounce(async () => {
await endpoint.read(poll.read.cluster, readAttrs);
try {
await endpoint.read(poll.read.cluster, readAttrs);
} catch (error) {
logger.error(`Failed to poll ${readAttrs} from ` +
`${this.zigbee.resolveEntity(endpoint.getDevice()).name}`);
}
}, 1000);
}
@@ -462,5 +502,3 @@ class Bind extends Extension {
}
}
}
module.exports = Bind;
+128 -88
View File
@@ -1,22 +1,29 @@
/* eslint-disable camelcase */
import logger from '../util/logger';
import * as utils from '../util/utils';
import utils from '../util/utils';
import * as settings from '../util/settings';
import Transport from 'winston-transport';
import bind from 'bind-decorator';
// @ts-ignore
import stringify from 'json-stable-stringify-without-jsonify';
import objectAssignDeep from 'object-assign-deep';
import {detailedDiff} from 'deep-object-diff';
import ExtensionTS from './extensionts';
import Extension from './extension';
import Device from '../model/device';
import Group from '../model/group';
import data from '../util/data';
import JSZip from 'jszip';
import fs from 'fs';
const requestRegex = new RegExp(`${settings.get().mqtt.base_topic}/bridge/request/(.*)`);
class Bridge extends ExtensionTS {
type Scene = {id: number, name: string};
type DefinitionPayload = {
model: string, vendor: string, description: string, exposes: zhc.DefinitionExpose[], supports_ota:
boolean, icon: string, options: zhc.DefinitionExpose[],
};
export default class Bridge extends Extension {
private zigbee2mqttVersion: {commitHash: string, version: string};
private coordinatorVersion: CoordinatorVersion;
private coordinatorVersion: zh.CoordinatorVersion;
private restartRequired = false;
private lastJoinedDeviceIeeeAddr: string;
private requestLookup: {[key: string]: (message: KeyValue | string) => Promise<MQTTResponse>};
@@ -33,8 +40,10 @@ class Bridge extends ExtensionTS {
'group/rename': this.groupRename,
'permit_join': this.permitJoin,
'restart': this.restart,
'backup': this.backup,
'touchlink/factory_reset': this.touchlinkFactoryReset,
'touchlink/identify': this.touchlinkIdentify,
'install_code/add': this.installCodeAdd,
'touchlink/scan': this.touchlinkScan,
'health_check': this.healthCheck,
'options': this.bridgeOptions,
@@ -53,16 +62,20 @@ class Bridge extends ExtensionTS {
callback();
}
}
// @ts-ignore TODO fix typing
logger.addTransport(new EventTransport());
this.zigbee2mqttVersion = await utils.getZigbee2MQTTVersion();
this.coordinatorVersion = await this.zigbee.getCoordinatorVersion();
this.eventBus.onDeviceRenamed(this, () => this.publishInfo());
this.eventBus.onEntityRenamed(this, () => this.publishInfo());
this.eventBus.onGroupMembersChanged(this, () => this.publishGroups());
this.eventBus.onDevicesChanged(this, () => this.publishDevices() && this.publishInfo());
this.eventBus.onPermitJoinChanged(this, () => !this.zigbee.isStopping() && this.publishInfo());
this.eventBus.onScenesChanged(this, () => {
this.publishDevices();
this.publishGroups();
});
// Zigbee events
const publishEvent = (type: string, data: KeyValue): Promise<void> =>
@@ -74,8 +87,7 @@ class Bridge extends ExtensionTS {
});
this.eventBus.onDeviceLeave(this, (data) => {
this.publishDevices();
publishEvent('device_leave',
{ieee_address: data.ieeeAddr, friendly_name: settings.getDevice(data.ieeeAddr)?.friendlyName});
publishEvent('device_leave', {ieee_address: data.ieeeAddr, friendly_name: data.name});
});
this.eventBus.onDeviceNetworkAddressChanged(this, () => this.publishDevices());
this.eventBus.onDeviceInterview(this, (data) => {
@@ -97,13 +109,10 @@ class Bridge extends ExtensionTS {
await this.publishDevices();
await this.publishGroups();
this.onMQTTMessage_ = this.onMQTTMessage_.bind(this);
this.eventBus.onMQTTMessage(this, this.onMQTTMessage_);
this.eventBus.onMQTTMessage(this, this.onMQTTMessage);
}
// TODO remove trailing _
@bind async onMQTTMessage_(data: EventMQTTMessage): Promise<void> {
@bind async onMQTTMessage(data: eventdata.MQTTMessage): Promise<void> {
const match = data.topic.match(requestRegex);
const key = match?.[1]?.toLowerCase();
if (key in this.requestLookup) {
@@ -114,6 +123,7 @@ class Bridge extends ExtensionTS {
await this.mqtt.publish(`bridge/response/${match[1]}`, stringify(response));
} catch (error) {
logger.error(`Request '${data.topic}' failed with error: '${error.message}'`);
logger.debug(error.stack);
const response = utils.getResponse(message, {}, error.message);
await this.mqtt.publish(`bridge/response/${match[1]}`, stringify(response));
}
@@ -137,35 +147,7 @@ class Bridge extends ExtensionTS {
throw new Error(`Invalid payload`);
}
const diff: KeyValue = detailedDiff(settings.get(), message.options);
// 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(message.options, diff.deleted);
const newSettings = objectAssignDeep({}, diff.added, diff.updated, 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(message.options, newSettings);
const newSettings = utils.computeSettingsToChange(settings.get(), message.options);
const restartRequired = settings.apply(newSettings);
if (restartRequired) this.restartRequired = true;
@@ -179,11 +161,10 @@ class Bridge extends ExtensionTS {
}
if (newSettings.hasOwnProperty('advanced') && newSettings.advanced.hasOwnProperty('log_level')) {
// @ts-ignore fix type
logger.setLevel(newSettings.advanced.log_level);
}
logger.info('Succesfully changed options');
logger.info('Successfully changed options');
this.publishInfo();
return utils.getResponse(message, {restart_required: this.restartRequired}, null);
}
@@ -210,7 +191,7 @@ class Bridge extends ExtensionTS {
const group = settings.addGroup(friendlyName, ID);
this.zigbee.createGroup(group.ID);
this.publishGroups();
return utils.getResponse(message, {friendly_name: group.friendlyName, id: group.ID}, null);
return utils.getResponse(message, {friendly_name: group.friendly_name, id: group.ID}, null);
}
@bind async deviceRename(message: string | KeyValue): Promise<MQTTResponse> {
@@ -228,6 +209,28 @@ class Bridge extends ExtensionTS {
return utils.getResponse(message, {}, null);
}
@bind async backup(message: string | KeyValue): Promise<MQTTResponse> {
await this.zigbee.backup();
const dataPath = data.getPath();
const files = utils.getAllFiles(dataPath).map((f) => [f, f.substring(dataPath.length + 1)])
.filter((f) => !f[1].startsWith('log'));
const zip = new JSZip();
files.forEach((f) => zip.file(f[1], fs.readFileSync(f[0])));
const base64Zip = await zip.generateAsync({type: 'base64'});
return utils.getResponse(message, {zip: base64Zip}, null);
}
@bind async installCodeAdd(message: KeyValue | string): Promise<MQTTResponse> {
if (typeof message === 'object' && !message.hasOwnProperty('value')) {
throw new Error('Invalid payload');
}
const value = typeof message === 'object' ? message.value : message;
await this.zigbee.addInstallCode(value);
logger.info('Successfully added new install code');
return utils.getResponse(message, {value}, null);
}
@bind async permitJoin(message: KeyValue | string): Promise<MQTTResponse> {
if (typeof message === 'object' && !message.hasOwnProperty('value')) {
throw new Error('Invalid payload');
@@ -305,12 +308,11 @@ class Bridge extends ExtensionTS {
// Deprecated
@bind async configLogLevel(message: KeyValue | string): Promise<MQTTResponse> {
const allowed = ['error', 'warn', 'info', 'debug'];
const value = this.getValue(message);
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}`);
}
// @ts-ignore TODO fix type
logger.setLevel(value);
this.publishInfo();
return utils.getResponse(message, {value}, null);
@@ -388,14 +390,23 @@ class Bridge extends ExtensionTS {
const ID = message.id;
const entity = this.getEntity(entityType, ID);
const oldOptions = objectAssignDeep({}, cleanup(entity.settings));
settings.changeEntityOptions(ID, message.options);
const newOptions = cleanup(settings.getEntity(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);
if (restartRequired) this.restartRequired = true;
const newOptions = cleanup(entity.options);
await this.publishInfo();
logger.info(`Changed config for ${entityType} ${ID}`);
return utils.getResponse(message, {from: oldOptions, to: newOptions, id: ID}, null);
this.eventBus.emitEntityOptionsChanged({from: oldOptions, to: newOptions, entity});
return utils.getResponse(
message,
{from: oldOptions, to: newOptions, id: ID, restart_required: this.restartRequired},
null,
);
}
@bind async deviceConfigureReporting(message: string | KeyValue): Promise<MQTTResponse> {
@@ -408,13 +419,13 @@ class Bridge extends ExtensionTS {
const parsedID = utils.parseEntityID(message.id);
const endpoint = (this.getEntity('device', parsedID.ID) as Device).endpoint(parsedID.endpoint);
const coordinatorEndpoint = this.zigbee.getFirstCoordinatorEndpoint();
const coordinatorEndpoint = this.zigbee.firstCoordinatorEndpoint();
await endpoint.bind(message.cluster, coordinatorEndpoint);
await endpoint.configureReporting(message.cluster, [{
attribute: message.attribute, minimumReportInterval: message.minimum_report_interval,
maximumReportInterval: message.maximum_report_interval, reportableChange: message.reportable_change,
}]);
}], message.options);
this.publishDevices();
@@ -443,23 +454,24 @@ class Bridge extends ExtensionTS {
const homeAssisantRename = message.hasOwnProperty('homeassistant_rename') ?
message.homeassistant_rename : false;
const entity = this.getEntity(entityType, from);
const oldFriendlyName = entity.settings.friendlyName;
const oldFriendlyName = entity.options.friendly_name;
settings.changeFriendlyName(from, to);
// Clear retained messages
this.mqtt.publish(oldFriendlyName, '', {retain: true});
this.eventBus.emitEntityRenamed({entity: entity, homeAssisantRename, from: oldFriendlyName, to});
if (entity instanceof Device) {
this.publishDevices();
this.eventBus.emit(`deviceRenamed`, {device: entity, homeAssisantRename, from: oldFriendlyName, to});
} else {
this.publishGroups();
this.publishInfo();
}
// Repulish entity state
this.publishEntityState(to, {});
// Republish entity state
this.publishEntityState(entity, {});
return utils.getResponse(
message,
@@ -488,28 +500,31 @@ class Bridge extends ExtensionTS {
}
try {
logger.info(`Removing ${entityType} '${entity.settings.friendlyName}'${blockForceLog}`);
logger.info(`Removing ${entityType} '${entity.name}'${blockForceLog}`);
const ieeeAddr = entity.isDevice() && entity.ieeeAddr;
const name = entity.name;
if (entity instanceof Device) {
if (block) {
settings.blockDevice(entity.ieeeAddr);
}
if (force) {
await entity.zhDevice.removeFromDatabase();
await entity.zh.removeFromDatabase();
} else {
await entity.zhDevice.removeFromNetwork();
await entity.zh.removeFromNetwork();
}
} else {
if (force) {
entity.zhGroup.removeFromDatabase();
entity.zh.removeFromDatabase();
} else {
await entity.zhGroup.removeFromNetwork();
await entity.zh.removeFromNetwork();
}
}
// Fire event
if (entity instanceof Device) {
this.eventBus.emit('deviceRemoved', {resolvedEntity: entity});
this.eventBus.emitDeviceRemoved({ieeeAddr, name});
}
// Remove from configuration.yaml
@@ -558,9 +573,11 @@ class Bridge extends ExtensionTS {
const payload = {
version: this.zigbee2mqttVersion.version,
commit: this.zigbee2mqttVersion.commitHash,
coordinator: this.coordinatorVersion,
coordinator: {
ieee_address: this.zigbee.firstCoordinatorEndpoint().getDevice().ieeeAddr,
...this.coordinatorVersion,
},
network: utils.toSnakeCase(await this.zigbee.getNetworkParameters()),
// @ts-ignore TODO fix type
log_level: logger.getLevel(),
permit_join: this.zigbee.getPermitJoin(),
permit_join_timeout: this.zigbee.getPermitJoinTimeout(),
@@ -573,18 +590,38 @@ class Bridge extends ExtensionTS {
'bridge/info', stringify(payload), {retain: true, qos: 0}, settings.get().mqtt.base_topic, true);
}
private getScenes(entity: zh.Endpoint | zh.Group): Scene[] {
const scenes: {[id: number]: Scene} = {};
const endpoints = utils.isEndpoint(entity) ? [entity] : entity.members;
const groupID = utils.isEndpoint(entity) ? 0 : entity.groupID;
for (const endpoint of endpoints) {
for (const [key, data] of Object.entries(endpoint.meta?.scenes || {})) {
const split = key.split('_');
const sceneID = parseInt(split[0], 10);
const sceneGroupID = parseInt(split[1], 10);
if (sceneGroupID === groupID) {
scenes[sceneID] = {id: sceneID, name: (data as KeyValue).name || `Scene ${sceneID}`};
}
}
}
return Object.values(scenes);
}
async publishDevices(): Promise<void> {
interface Data {
bindings: {cluster: string, target: {type: string, endpoint?: number, ieee_address?: string, id?: number}}[]
configured_reportings: {cluster: string, attribute: string | number,
minimum_report_interval: number, maximum_report_interval: number, reportable_change: number}[],
clusters: {input: string[], output: string[]}
clusters: {input: string[], output: string[]}, scenes: Scene[]
}
const devices = this.zigbee.getDevices().map((device) => {
const devices = this.zigbee.devices().map((device) => {
const endpoints: {[s: number]: Data} = {};
for (const endpoint of device.endpoints) {
for (const endpoint of device.zh.endpoints) {
const data: Data = {
scenes: this.getScenes(endpoint),
bindings: [],
configured_reportings: [],
clusters: {
@@ -615,18 +652,20 @@ class Bridge extends ExtensionTS {
return {
ieee_address: device.ieeeAddr,
type: device.type,
network_address: device.networkAddress,
type: device.zh.type,
network_address: device.zh.networkAddress,
supported: !!device.definition,
friendly_name: device.name,
disabled: !!device.options.disabled,
description: device.options.description,
definition: this.getDefinitionPayload(device),
power_source: device.powerSource,
software_build_id: device.softwareBuildID,
date_code: device.dateCode,
model_id: device.modelID,
interviewing: device.interviewing,
interview_completed: device.interviewCompleted,
manufacturer: device.manufacturerName,
power_source: device.zh.powerSource,
software_build_id: device.zh.softwareBuildID,
date_code: device.zh.dateCode,
model_id: device.zh.modelID,
interviewing: device.zh.interviewing,
interview_completed: device.zh.interviewCompleted,
manufacturer: device.zh.manufacturerName,
endpoints,
};
});
@@ -636,11 +675,13 @@ class Bridge extends ExtensionTS {
}
async publishGroups(): Promise<void> {
const groups = this.zigbee.getGroups().map((g) => {
const groups = this.zigbee.groups().map((g) => {
return {
id: g.ID,
friendly_name: g.ID === 901 ? 'default_bind_group' : g.name,
members: g.members.map((e) => {
description: g.options.description,
scenes: this.getScenes(g.zh),
members: g.zh.members.map((e) => {
return {ieee_address: e.getDevice().ieeeAddr, endpoint: e.ID};
}),
};
@@ -649,23 +690,22 @@ class Bridge extends ExtensionTS {
'bridge/groups', stringify(groups), {retain: true, qos: 0}, settings.get().mqtt.base_topic, true);
}
getDefinitionPayload(device: Device):
{model: string, vendor: string, description: string, exposes: unknown[], supports_ota: boolean, icon: string} {
getDefinitionPayload(device: Device): DefinitionPayload {
if (!device.definition) return null;
let icon = device.settings.icon ? device.settings.icon : device.definition.icon;
let icon = device.options.icon ? device.options.icon : device.definition.icon;
if (icon) {
icon = icon.replace('${zigbeeModel}', utils.sanitizeImageParameter(device.modelID));
icon = icon.replace('${zigbeeModel}', utils.sanitizeImageParameter(device.zh.modelID));
icon = icon.replace('${model}', utils.sanitizeImageParameter(device.definition.model));
}
return {
model: device.definition.model,
vendor: device.definition.vendor,
description: device.definition.description,
exposes: device.definition.exposes,
exposes: device.exposes(),
supports_ota: !!device.definition.ota,
options: device.definition.options,
icon,
};
}
}
module.exports = Bridge;
+30 -34
View File
@@ -1,37 +1,32 @@
import * as settings from '../util/settings';
import * as utils from '../util/utils';
import utils from '../util/utils';
import logger from '../util/logger';
// @ts-ignore
import stringify from 'json-stable-stringify-without-jsonify';
// @ts-ignore
import zhc from 'zigbee-herdsman-converters';
import ExtensionTS from './extensionts';
import Extension from './extension';
import bind from 'bind-decorator';
import Device from '../model/device';
/**
* This extension calls the zigbee-herdsman-converters definition configure() method
*/
class Configure extends ExtensionTS {
export default class Configure extends Extension {
private configuring = new Set();
private attempts: {[s: string]: number} = {};
private topic = `${settings.get().mqtt.base_topic}/bridge/request/device/configure`;
private legacyTopic = `${settings.get().mqtt.base_topic}/bridge/configure`;
@bind private async onReportingDisabled(data: EventReportingDisabled): Promise<void> {
@bind private async onReconfigure(data: eventdata.Reconfigure): Promise<void> {
// Disabling reporting unbinds some cluster which could be bound by configure, re-setup.
const device = this.zigbee.resolveEntity(data.device) as Device; // TODO assert device
if (device.zhDevice.meta?.hasOwnProperty('configured')) {
delete device.zhDevice.meta.configured;
device.zhDevice.save();
if (data.device.zh.meta?.hasOwnProperty('configured')) {
delete data.device.zh.meta.configured;
data.device.zh.save();
}
await this.configure(device, 'reporting_disabled');
await this.configure(data.device, 'reporting_disabled');
}
// TODO remove trailing _
@bind private async onMQTTMessage_(data: EventMQTTMessage): Promise<void> {
@bind private async onMQTTMessage(data: eventdata.MQTTMessage): Promise<void> {
if (data.topic === this.legacyTopic) {
const device = this.zigbee.resolveEntity(data.message);
if (!device || !(device instanceof Device)) {
@@ -69,37 +64,40 @@ class Configure extends ExtensionTS {
}
override async start(): Promise<void> {
for (const device of this.zigbee.getClients()) {
await this.configure(device, 'started');
}
setImmediate(async () => {
for (const device of this.zigbee.devices(false)) {
await this.configure(device, 'started');
}
});
this.eventBus.onDeviceJoined(this, (data) => {
if (data.device.zhDevice.meta.hasOwnProperty('configured')) {
delete data.device.zhDevice.meta.configured;
data.device.zhDevice.save();
if (data.device.zh.meta.hasOwnProperty('configured')) {
delete data.device.zh.meta.configured;
data.device.zh.save();
}
this.configure(data.device, 'zigbee_event');
});
this.eventBus.onDeviceInterview(this, (data) => this.configure(data.device, 'zigbee_event'));
this.eventBus.onLastSeenChanged(this, (data) => this.configure(data.device, 'zigbee_event'));
this.eventBus.onMQTTMessage(this, this.onMQTTMessage_);
this.eventBus.onReportingDisabled(this, this.onReportingDisabled);
this.eventBus.onMQTTMessage(this, this.onMQTTMessage);
this.eventBus.onReconfigure(this, this.onReconfigure);
}
private async configure(device: Device, event: 'started' | 'zigbee_event' | 'reporting_disabled' | 'mqtt_message',
force=false, thowError=false): Promise<void> {
force=false, throwError=false): Promise<void> {
if (!force) {
if (!device.definition?.configure || device.zhDevice.interviewing) {
if (device.options.disabled || !device.definition?.configure || !device.zh.interviewCompleted) {
return;
}
if (device.zhDevice.meta?.hasOwnProperty('configured') &&
device.zhDevice.meta.configured === zhc.getConfigureKey(device.definition)) {
if (device.zh.meta?.hasOwnProperty('configured') &&
device.zh.meta.configured === zhc.getConfigureKey(device.definition)) {
return;
}
// Only configure end devices when it is active, otherwise it will likely fails as they are sleeping.
if (device.zhDevice.type === 'EndDevice' && event !== 'zigbee_event') {
if (device.zh.type === 'EndDevice' && event !== 'zigbee_event') {
return;
}
}
@@ -116,19 +114,19 @@ class Configure extends ExtensionTS {
logger.info(`Configuring '${device.name}'`);
try {
await device.definition.configure(device.zhDevice, this.zigbee.getFirstCoordinatorEndpoint(), logger);
await device.definition.configure(device.zh, this.zigbee.firstCoordinatorEndpoint(), logger,
device.options);
logger.info(`Successfully configured '${device.name}'`);
device.zhDevice.meta.configured = zhc.getConfigureKey(device.definition);
device.zhDevice.save();
device.zh.meta.configured = zhc.getConfigureKey(device.definition);
device.zh.save();
this.eventBus.emitDevicesChanged();
this.eventBus.emit(`devicesChanged`);
} catch (error) {
this.attempts[device.ieeeAddr]++;
const attempt = this.attempts[device.ieeeAddr];
const msg = `Failed to configure '${device.name}', attempt ${attempt} (${error.stack})`;
logger.error(msg);
if (thowError) {
if (throwError) {
throw error;
}
}
@@ -136,5 +134,3 @@ class Configure extends ExtensionTS {
this.configuring.delete(device.ieeeAddr);
}
}
module.exports = Configure;
-54
View File
@@ -1,54 +0,0 @@
class Extension {
/**
* Besides intializing variables, the constructor should do nothing!
*
* @param {Zigbee} zigbee Zigbee controller
* @param {MQTT} mqtt MQTT controller
* @param {State} state State controller
* @param {Function} publishEntityState Method to publish device state to MQTT.
* @param {EventBus} eventBus The event bus
*/
constructor(zigbee, mqtt, state, publishEntityState, eventBus) {
this.zigbee = zigbee;
this.mqtt = mqtt;
this.state = state;
this.publishEntityState = publishEntityState;
this.eventBus = eventBus;
}
/**
* This method is called by the controller once Zigbee has been started.
*/
// onZigbeeStarted() {}
/**
* This method is called by the controller once connected to the MQTT server.
*/
// onMQTTConnected() {}
/**
* Is called when a Zigbee message from a device is received.
* @param {string} type Type of the message
* @param {Object} data Data of the message
* @param {Object?} resolvedEntity Resolved entity returned from this.zigbee.resolveEntityLegacy()
* @param {Object?} settingsDevice Device settings
*/
// onZigbeeEvent(type, data, resolvedEntity) {}
/**
* Is called when a MQTT message is received
* @param {string} topic Topic on which the message was received
* @param {Object} message The received message
* @return {boolean} if the message was handled
*/
// onMQTTMessage(topic, message) {}
/**
* Is called once the extension has to stop
*/
stop() {
this.eventBus.removeListeners(this.constructor.name);
}
}
module.exports = Extension;
@@ -1,13 +1,12 @@
// TODO: tempState -> State, rename to extension
abstract class ExtensionTS {
abstract class Extension {
protected zigbee: Zigbee;
protected mqtt: MQTT;
protected state: TempState;
protected state: State;
protected publishEntityState: PublishEntityState;
protected eventBus: EventBus;
protected enableDisableExtension: (enable: boolean, name: string) => Promise<void>;
protected restartCallback: () => void;
protected addExtension: (extension: ExternalConverterClass) => void;
protected addExtension: (extension: Extension) => Promise<void>;
/**
* Besides intializing variables, the constructor should do nothing!
@@ -21,9 +20,9 @@ abstract class ExtensionTS {
* @param {restartCallback} restartCallback Restart Zigbee2MQTT
* @param {addExtension} addExtension Add an extension
*/
constructor(zigbee: Zigbee, mqtt: MQTT, state: TempState, publishEntityState: PublishEntityState,
constructor(zigbee: Zigbee, mqtt: MQTT, state: State, publishEntityState: PublishEntityState,
eventBus: EventBus, enableDisableExtension: (enable: boolean, name: string) => Promise<void>,
restartCallback: () => void, addExtension: (extension: ExternalConverterClass) => void) {
restartCallback: () => void, addExtension: (extension: Extension) => Promise<void>) {
this.zigbee = zigbee;
this.mqtt = mqtt;
this.state = state;
@@ -46,6 +45,9 @@ abstract class ExtensionTS {
async stop(): Promise<void> {
this.eventBus.removeListeners(this);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public adjustMessageBeforePublish(entity: Group | Device, message: KeyValue): void {}
}
export default ExtensionTS;
export default Extension;
+9 -8
View File
@@ -1,17 +1,18 @@
// @ts-ignore
import zhc from 'zigbee-herdsman-converters';
import * as settings from '../util/settings';
import * as utils from '../util/utils';
import ExtensionTS from './extensionts';
import utils from '../util/utils';
import Extension from './extension';
class ExternalConverters extends ExtensionTS {
override async start(): Promise<void> {
for (const definition of utils.getExternalConvertersDefinitions(settings)) {
export default class ExternalConverters extends Extension {
constructor(zigbee: Zigbee, mqtt: MQTT, state: State, publishEntityState: PublishEntityState,
eventBus: EventBus, enableDisableExtension: (enable: boolean, name: string) => Promise<void>,
restartCallback: () => void, addExtension: (extension: Extension) => Promise<void>) {
super(zigbee, mqtt, state, publishEntityState, eventBus, enableDisableExtension, restartCallback, addExtension);
for (const definition of utils.getExternalConvertersDefinitions(settings.get())) {
const toAdd = {...definition};
delete toAdd['homeassistant'];
zhc.addDeviceDefinition(toAdd);
}
}
}
module.exports = ExternalConverters;
+23 -25
View File
@@ -1,21 +1,20 @@
import * as settings from '../util/settings';
import * as utils from '../util/utils';
import utils from '../util/utils';
import fs from 'fs';
import * as data from './../util/data';
import data from './../util/data';
import path from 'path';
import logger from './../util/logger';
// @ts-ignore
import stringify from 'json-stable-stringify-without-jsonify';
import bind from 'bind-decorator';
import ExtensionTS from './extensionts';
import Extension from './extension';
const requestRegex = new RegExp(`${settings.get().mqtt.base_topic}/bridge/request/extension/(save|remove)`);
class ExternalExtension extends ExtensionTS {
private requestLookup: {[s: string]: (message: KeyValue) => MQTTResponse};
export default class ExternalExtension extends Extension {
private requestLookup: {[s: string]: (message: KeyValue) => Promise<MQTTResponse>};
override async start(): Promise<void> {
this.eventBus.onMQTTMessage(this, this.onMQTTMessage_);
this.eventBus.onMQTTMessage(this, this.onMQTTMessage);
this.requestLookup = {'save': this.saveExtension, 'remove': this.removeExtension};
this.loadUserDefinedExtensions();
await this.publishExtensions();
@@ -29,21 +28,21 @@ class ExternalExtension extends ExtensionTS {
const basePath = this.getExtensionsBasePath();
if (fs.existsSync(basePath)) {
return fs.readdirSync(basePath).filter((f) => f.endsWith('.js')).map((fileName) => {
const extensonFilePath = path.join(basePath, fileName);
return {'name': fileName, 'code': fs.readFileSync(extensonFilePath, 'utf-8')};
const extensionFilePath = path.join(basePath, fileName);
return {'name': fileName, 'code': fs.readFileSync(extensionFilePath, 'utf-8')};
});
} else {
return [];
}
}
@bind private removeExtension(message: KeyValue): MQTTResponse {
@bind private async removeExtension(message: KeyValue): Promise<MQTTResponse> {
const {name} = message;
const extensions = this.getListOfUserDefinedExtensions();
const extensionToBeRemoved = extensions.find((e) => e.name === name);
if (extensionToBeRemoved) {
this.enableDisableExtension(false, extensionToBeRemoved.name);
await this.enableDisableExtension(false, extensionToBeRemoved.name);
const basePath = this.getExtensionsBasePath();
const extensionFilePath = path.join(basePath, path.basename(name));
fs.unlinkSync(extensionFilePath);
@@ -55,28 +54,28 @@ class ExternalExtension extends ExtensionTS {
}
}
@bind private saveExtension(message: KeyValue): MQTTResponse {
@bind private async saveExtension(message: KeyValue): Promise<MQTTResponse> {
const {name, code} = message;
const ModuleConstructor = utils.loadModuleFromText(code) as ExternalConverterClass;
this.loadExtension(ModuleConstructor);
const ModuleConstructor = utils.loadModuleFromText(code, name) as typeof Extension;
await this.loadExtension(ModuleConstructor);
const basePath = this.getExtensionsBasePath();
/* istanbul ignore else */
if (!fs.existsSync(basePath)) {
fs.mkdirSync(basePath);
}
const extensonFilePath = path.join(basePath, path.basename(name));
fs.writeFileSync(extensonFilePath, code);
const extensionFilePath = path.join(basePath, path.basename(name));
fs.writeFileSync(extensionFilePath, code);
this.publishExtensions();
logger.info(`Extension ${name} loaded`);
return utils.getResponse(message, {}, null);
}
@bind async onMQTTMessage_(data: EventMQTTMessage): Promise<void> {
@bind async onMQTTMessage(data: eventdata.MQTTMessage): Promise<void> {
const match = data.topic.match(requestRegex);
if (match && this.requestLookup[match[1].toLowerCase()]) {
const message = utils.parseJSON(data.message, data.message) as KeyValue;
try {
const response = this.requestLookup[match[1].toLowerCase()](message);
const response = await this.requestLookup[match[1].toLowerCase()](message);
await this.mqtt.publish(`bridge/response/extension/${match[1]}`, stringify(response));
} catch (error) {
logger.error(`Request '${data.topic}' failed with error: '${error.message}'`);
@@ -86,16 +85,17 @@ class ExternalExtension extends ExtensionTS {
}
}
@bind private loadExtension(ConstructorClass: ExternalConverterClass): void {
this.enableDisableExtension(false, ConstructorClass.name);
this.addExtension(new ConstructorClass(
this.zigbee, this.mqtt, this.state, this.publishEntityState, this.eventBus, settings, logger));
@bind private async loadExtension(ConstructorClass: typeof Extension): Promise<void> {
await this.enableDisableExtension(false, ConstructorClass.name);
// @ts-ignore
await this.addExtension(new ConstructorClass(this.zigbee, this.mqtt, this.state, this.publishEntityState,
this.eventBus, settings, logger));
}
private loadUserDefinedExtensions(): void {
const extensions = this.getListOfUserDefinedExtensions();
extensions
.map(({code}) => utils.loadModuleFromText(code))
.map(({code, name}) => utils.loadModuleFromText(code, name))
.map(this.loadExtension);
}
@@ -107,5 +107,3 @@ class ExternalExtension extends ExtensionTS {
}, settings.get().mqtt.base_topic, true);
}
}
module.exports = ExternalExtension;
+66 -40
View File
@@ -1,67 +1,99 @@
import http from 'http';
import serveStatic from 'serve-static';
import https from 'https';
import gzipStatic, {RequestHandler} from 'connect-gzip-static';
import finalhandler from 'finalhandler';
import logger from '../util/logger';
// @ts-ignore
import frontend from 'zigbee2mqtt-frontend';
import WebSocket from 'ws';
import net from 'net';
import url from 'url';
import fs from 'fs';
import * as settings from '../util/settings';
import * as utils from '../util/utils';
// @ts-ignore
import utils from '../util/utils';
import stringify from 'json-stable-stringify-without-jsonify';
import ExtensionTS from './extensionts';
import Extension from './extension';
import bind from 'bind-decorator';
/**
* This extension servers the frontend
*/
class Frontend extends ExtensionTS {
export default class Frontend extends Extension {
private mqttBaseTopic = settings.get().mqtt.base_topic;
private host = settings.get().frontend.host || '0.0.0.0';
private port = settings.get().frontend.port || 8080;
private authToken = settings.get().frontend.auth_token || false;
private host = settings.get().frontend.host;
private port = settings.get().frontend.port;
private sslCert = settings.get().frontend.ssl_cert;
private sslKey = settings.get().frontend.ssl_key;
private authToken = settings.get().frontend.auth_token;
private retainedMessages = new Map();
private server: http.Server;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private fileServer: serveStatic.RequestHandler<any>;
private fileServer: RequestHandler;
private wss: WebSocket.Server = null;
constructor(zigbee: Zigbee, mqtt: MQTT, state: TempState, publishEntityState: PublishEntityState,
constructor(zigbee: Zigbee, mqtt: MQTT, state: State, publishEntityState: PublishEntityState,
eventBus: EventBus, enableDisableExtension: (enable: boolean, name: string) => Promise<void>,
restartCallback: () => void, addExtension: (extension: ExternalConverterClass) => void) {
restartCallback: () => void, addExtension: (extension: Extension) => Promise<void>) {
super(zigbee, mqtt, state, publishEntityState, eventBus, enableDisableExtension, restartCallback, addExtension);
this.eventBus.onMQTTMessagePublished(this, this.onMQTTPublishMessage);
}
private isHttpsConfigured():boolean {
if (this.sslCert && this.sslKey) {
if (!fs.existsSync(this.sslCert) || !fs.existsSync(this.sslKey)) {
logger.error(`defined ssl_cert '${this.sslCert}' or ssl_key '${this.sslKey}' file path does not exists, server won't be secured.`); /* eslint-disable-line max-len */
return false;
}
return true;
}
return false;
}
override async start(): Promise<void> {
this.server = http.createServer(this.onRequest);
if (this.isHttpsConfigured()) {
const serverOptions = {
key: fs.readFileSync(this.sslKey),
cert: fs.readFileSync(this.sslCert)};
this.server = https.createServer(serverOptions, this.onRequest);
} else {
this.server = http.createServer(this.onRequest);
}
this.server.on('upgrade', this.onUpgrade);
/* istanbul ignore next */ // eslint-disable-next-line @typescript-eslint/no-explicit-any
const options = {setHeaders: (res: any, path: any): void => {
if (path.endsWith('index.html')) {
res.setHeader('Cache-Control', 'no-store');
}
}};
this.fileServer = serveStatic(frontend.getPath(), options);
/* istanbul ignore next */
const options = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
setHeaders: (res: any, path: string): void => {
if (path.endsWith('index.html')) {
res.setHeader('Cache-Control', 'no-store');
}
},
};
this.fileServer = gzipStatic(frontend.getPath(), options);
this.wss = new WebSocket.Server({noServer: true});
this.wss.on('connection', this.onWebSocketConnection);
this.server.listen(this.port, this.host);
logger.info(`Started frontend on port ${this.host}:${this.port}`);
if (this.host.startsWith('/')) {
this.server.listen(this.host);
logger.info(`Started frontend on socket ${this.host}`);
} else {
this.server.listen(this.port, this.host);
logger.info(`Started frontend on port ${this.host}:${this.port}`);
}
}
override async stop(): Promise<void> {
super.stop();
for (const client of this.wss.clients) {
this.wss?.clients.forEach((client) => {
client.send(stringify({topic: 'bridge/state', payload: 'offline'}));
client.terminate();
});
this.wss?.close();
/* istanbul ignore else */
if (this.server) {
return new Promise((cb: () => void) => this.server.close(cb));
}
this.wss.close();
return new Promise((cb: () => void) => this.server.close(cb));
}
@bind private onRequest(request: http.IncomingMessage, response: http.ServerResponse): void {
@@ -76,8 +108,8 @@ class Frontend extends ExtensionTS {
@bind private onUpgrade(request: http.IncomingMessage, socket: net.Socket, head: Buffer): void {
this.wss.handleUpgrade(request, socket, head, (ws) => {
this.authenticate(request, (isAuthentificated) => {
if (isAuthentificated) {
this.authenticate(request, (isAuthenticated) => {
if (isAuthenticated) {
this.wss.emit('connection', ws, request);
} else {
ws.close(4401, 'Unauthorized');
@@ -99,27 +131,23 @@ class Frontend extends ExtensionTS {
ws.send(stringify({topic: key, payload: value}));
}
for (const device of this.zigbee.getClients()) {
let payload: KeyValue = {};
if (this.state.exists(device.ieeeAddr)) {
payload = {...payload, ...this.state.get(device.ieeeAddr)};
}
for (const device of this.zigbee.devices(false)) {
const payload = this.state.get(device);
const lastSeen = settings.get().advanced.last_seen;
/* istanbul ignore if */
if (lastSeen !== 'disable') {
payload.last_seen = utils.formatDate(device.lastSeen, lastSeen);
payload.last_seen = utils.formatDate(device.zh.lastSeen, lastSeen);
}
if (device.zhDevice.linkquality !== undefined) {
payload.linkquality = device.zhDevice.linkquality;
if (device.zh.linkquality !== undefined) {
payload.linkquality = device.zh.linkquality;
}
ws.send(stringify({topic: device.name, payload}));
}
}
@bind private onMQTTPublishMessage(data: EventMQTTMessagePublished): void {
@bind private onMQTTPublishMessage(data: eventdata.MQTTMessagePublished): void {
if (data.topic.startsWith(`${this.mqttBaseTopic}/`)) {
// Send topic without base_topic
const topic = data.topic.substring(this.mqttBaseTopic.length + 1);
@@ -139,5 +167,3 @@ class Frontend extends ExtensionTS {
}
}
}
module.exports = Frontend;
-377
View File
@@ -1,377 +0,0 @@
const settings = require('../util/settings');
const logger = require('../util/logger');
const Extension = require('./extension');
const utils = require('../util/utils');
const stringify = require('json-stable-stringify-without-jsonify');
const equals = require('fast-deep-equal/es6');
const topicRegex =
new RegExp(`^${settings.get().mqtt.base_topic}/bridge/request/group/members/(remove|add|remove_all)$`);
const legacyTopicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/group/(.+)/(remove|add|remove_all)$`);
const legacyTopicRegexRemoveAll = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/group/remove_all$`);
const stateProperties = {
'state': (value, definition) => true,
'brightness': (value, definition) =>
definition.exposes.find((e) => e.type === 'light' && e.features.find((f) => f.name === 'brightness')),
'color_temp': (value, definition) =>
definition.exposes.find((e) => e.type === 'light' && e.features.find((f) => f.name === 'color_temp')),
'color': (value, definition) =>
definition.exposes.find((e) => e.type === 'light' &&
e.features.find((f) => f.name === 'color_xy' || f.name === 'color_hs')),
'color_mode': (value, definition) =>
definition.exposes.find((e) => e.type === 'light' && (
(e.features.find((f) => f.name === `color_${value}`)) ||
(value === 'color_temp' && e.features.find((f) => f.name === 'color_temp')) )),
};
class Groups extends Extension {
constructor(zigbee, mqtt, state, publishEntityState, eventBus) {
super(zigbee, mqtt, state, publishEntityState, eventBus);
this.onStateChange = this.onStateChange.bind(this);
this.legacyApi = settings.get().advanced.legacy_api;
this.lastOptimisticState = {};
}
async onZigbeeStarted() {
this.eventBus.on('stateChange', this.onStateChange, this.constructor.name);
await this.syncGroupsWithSettings();
}
async syncGroupsWithSettings() {
const settingsGroups = settings.getGroups();
const zigbeeGroups = this.zigbee.getGroupsLegacy();
const addRemoveFromGroup = async (action, deviceName, groupName, endpoint, group) => {
try {
logger.info(`${action === 'add' ? 'Adding' : 'Removing'} '${deviceName}' to group '${groupName}'`);
if (action === 'remove') {
await endpoint.removeFromGroup(group);
} else {
await endpoint.addToGroup(group);
}
} catch (error) {
logger.error(`Failed to ${action} '${deviceName}' from '${groupName}'`);
logger.debug(error.stack);
}
};
for (const settingGroup of settingsGroups) {
const groupID = settingGroup.ID;
const zigbeeGroup = zigbeeGroups.find((g) =>
g.groupID === groupID) || this.zigbee.createGroupLegacy(groupID);
const settingsEntity = settingGroup.devices.map((d) => {
const entity = this.zigbee.resolveEntityLegacy(d);
if (!entity) logger.error(`Cannot find '${d}' of group '${settingGroup.friendlyName}'`);
return entity;
}).filter((e) => e != null);
// In settings but not in zigbee
for (const entity of settingsEntity) {
if (!zigbeeGroup.hasMember(entity.endpoint)) {
addRemoveFromGroup('add', entity.name, settingGroup.friendlyName, entity.endpoint, zigbeeGroup);
}
}
// In zigbee but not in settings
for (const endpoint of zigbeeGroup.members) {
if (!settingsEntity.find((e) => e.endpoint === endpoint)) {
const deviceName = settings.getDevice(endpoint.getDevice().ieeeAddr).friendlyName;
addRemoveFromGroup('remove', deviceName, settingGroup.friendlyName, endpoint, zigbeeGroup);
}
}
}
for (const zigbeeGroup of zigbeeGroups) {
if (!settingsGroups.find((g) => g.ID === zigbeeGroup.groupID)) {
for (const endpoint of zigbeeGroup.members) {
const deviceName = settings.getDevice(endpoint.getDevice().ieeeAddr).friendlyName;
addRemoveFromGroup('remove', deviceName, zigbeeGroup.groupID, endpoint, zigbeeGroup);
}
}
}
}
async onStateChange(data) {
const reason = 'group_optimistic';
if (data.reason === reason) {
return;
}
const payload = {};
let endpointName;
for (let [prop, value] of Object.entries(data.update)) {
const endpointNameMatch = utils.endpointNames.find((n) => prop.endsWith(`_${n}`));
if (endpointNameMatch) {
prop = prop.substring(0, prop.length - endpointNameMatch.length - 1);
endpointName = endpointNameMatch;
}
if (prop in stateProperties) {
payload[prop] = value;
}
}
if (Object.keys(payload).length) {
const resolvedEntity =
this.zigbee.resolveEntityLegacy(`${data.ID}${endpointName ? `/${endpointName}` : ''}`);
const zigbeeGroups = this.zigbee.getGroupsLegacy().filter((zigbeeGroup) => {
const settingsGroup = settings.getGroup(zigbeeGroup.groupID);
return settingsGroup && (!settingsGroup.hasOwnProperty('optimistic') || settingsGroup.optimistic);
});
if (resolvedEntity.type === 'device') {
for (const zigbeeGroup of zigbeeGroups) {
if (zigbeeGroup.hasMember(resolvedEntity.endpoint) &&
!equals(this.lastOptimisticState[zigbeeGroup.groupID], payload)) {
if (!payload || payload.state !== 'OFF' || this.areAllMembersOff(zigbeeGroup)) {
this.lastOptimisticState[zigbeeGroup.groupID] = payload;
await this.publishEntityState(zigbeeGroup.groupID, payload, reason);
}
}
}
} else {
// Invalidate the last optimistic group state when group state is changed directly.
delete this.lastOptimisticState[resolvedEntity.group.groupID];
const groupIDsToPublish = new Set();
for (const member of resolvedEntity.group.members) {
const resolvedEntity = this.zigbee.resolveEntityLegacy(member);
const memberPayload = {};
Object.keys(payload).forEach((key) => {
if (stateProperties[key](payload[key], resolvedEntity.definition)) {
memberPayload[key] = payload[key];
}
});
const endpointName = resolvedEntity.endpointName;
if (endpointName) {
Object.keys(memberPayload).forEach((key) => {
memberPayload[`${key}_${endpointName}`] = memberPayload[key];
delete memberPayload[key];
});
}
await this.publishEntityState(member.getDevice().ieeeAddr, memberPayload, reason);
for (const zigbeeGroup of zigbeeGroups) {
if (zigbeeGroup.hasMember(member)) {
if (!payload || payload.state !== 'OFF' || this.areAllMembersOff(zigbeeGroup)) {
groupIDsToPublish.add(zigbeeGroup.groupID);
}
}
}
}
groupIDsToPublish.delete(resolvedEntity.group.groupID);
for (const groupID of groupIDsToPublish) {
await this.publishEntityState(groupID, payload, reason);
}
}
}
}
areAllMembersOff(zigbeeGroup) {
for (const member of zigbeeGroup.members) {
const device = member.getDevice();
if (this.state.exists(device.ieeeAddr)) {
const state = this.state.get(device.ieeeAddr);
if (state && state.state === 'ON') {
return false;
}
}
}
return true;
}
parseMQTTMessage(topic, message) {
let type = null;
let resolvedEntityGroup = null;
let resolvedEntityDevice = null;
let hasEndpointName = null;
let error = null;
let groupKey = null;
let deviceKey = null;
let triggeredViaLegacyApi = false;
let skipDisableReporting = false;
/* istanbul ignore else */
const topicRegexMatch = topic.match(topicRegex);
const legacyTopicRegexRemoveAllMatch = topic.match(legacyTopicRegexRemoveAll);
const legacyTopicRegexMatch = topic.match(legacyTopicRegex);
if (this.legacyApi && (legacyTopicRegexMatch || legacyTopicRegexRemoveAllMatch)) {
triggeredViaLegacyApi = true;
if (legacyTopicRegexMatch) {
resolvedEntityGroup = this.zigbee.resolveEntityLegacy(legacyTopicRegexMatch[1]);
type = legacyTopicRegexMatch[2];
if (!resolvedEntityGroup || resolvedEntityGroup.type !== 'group') {
logger.error(`Group '${legacyTopicRegexMatch[1]}' does not exist`);
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const payload = {
friendly_name: message, group: legacyTopicRegexMatch[1], error: 'group doesn\'t exists',
};
this.mqtt.publish(
'bridge/log',
stringify({type: `device_group_${type}_failed`, message: payload}),
);
}
return {};
}
} else {
type = 'remove_all';
}
resolvedEntityDevice = this.zigbee.resolveEntityLegacy(message);
if (!resolvedEntityDevice || !resolvedEntityDevice.type === 'device') {
logger.error(`Device '${message}' does not exist`);
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const payload = {
friendly_name: message, group: legacyTopicRegexMatch[1], error: 'entity doesn\'t exists',
};
this.mqtt.publish(
'bridge/log',
stringify({type: `device_group_${type}_failed`, message: payload}),
);
}
return {};
}
hasEndpointName = utils.endpointNames.find((p) => message.endsWith(`/${p}`));
} else if (topicRegexMatch) {
type = topicRegexMatch[1];
message = JSON.parse(message);
deviceKey = message.device;
skipDisableReporting = 'skip_disable_reporting' in message ? message.skip_disable_reporting : false;
if (type !== 'remove_all') {
groupKey = message.group;
resolvedEntityGroup = this.zigbee.resolveEntityLegacy(message.group);
if (!resolvedEntityGroup || resolvedEntityGroup.type !== 'group') {
error = `Group '${message.group}' does not exist`;
}
}
resolvedEntityDevice = this.zigbee.resolveEntityLegacy(message.device);
if (!error && (!resolvedEntityDevice || !resolvedEntityDevice.type === 'device')) {
error = `Device '${message.device}' does not exist`;
}
hasEndpointName = utils.endpointNames.find((p) => message.device.endsWith(`/${p}`));
}
return {
resolvedEntityGroup, resolvedEntityDevice, type, hasEndpointName, error, groupKey, deviceKey,
triggeredViaLegacyApi, skipDisableReporting,
};
}
async onMQTTMessage(topic, message) {
let {
resolvedEntityGroup, resolvedEntityDevice, type, hasEndpointName, error, triggeredViaLegacyApi,
groupKey, deviceKey, skipDisableReporting,
} = this.parseMQTTMessage(topic, message);
if (!type) return;
message = utils.parseJSON(message, message);
const responseData = {device: deviceKey};
if (groupKey) {
responseData.group = groupKey;
}
if (!error) {
try {
const keys = [
`${resolvedEntityDevice.device.ieeeAddr}/${resolvedEntityDevice.endpoint.ID}`,
`${resolvedEntityDevice.name}/${resolvedEntityDevice.endpoint.ID}`,
];
const definition = resolvedEntityDevice.definition;
const endpoints = definition && definition.endpoint ?
definition.endpoint(resolvedEntityDevice.device) : null;
let endpointName = null;
if (endpoints) {
const matches = Object.entries(endpoints).find((e) => e[1] === resolvedEntityDevice.endpoint.ID);
endpointName = matches && matches[0];
}
if (endpointName) {
keys.push(`${resolvedEntityDevice.device.ieeeAddr}/${endpointName}`);
keys.push(`${resolvedEntityDevice.name}/${endpointName}`);
}
if (!hasEndpointName) {
keys.push(resolvedEntityDevice.name);
keys.push(resolvedEntityDevice.device.ieeeAddr);
}
if (type === 'add') {
logger.info(`Adding '${resolvedEntityDevice.name}' to '${resolvedEntityGroup.name}'`);
await resolvedEntityDevice.endpoint.addToGroup(resolvedEntityGroup.group);
settings.addDeviceToGroup(resolvedEntityGroup.settings.ID, keys);
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const payload = {friendly_name: resolvedEntityDevice.name, group: resolvedEntityGroup.name};
this.mqtt.publish(
'bridge/log',
stringify({type: `device_group_add`, message: payload}),
);
}
} else if (type === 'remove') {
logger.info(`Removing '${resolvedEntityDevice.name}' from '${resolvedEntityGroup.name}'`);
await resolvedEntityDevice.endpoint.removeFromGroup(resolvedEntityGroup.group);
settings.removeDeviceFromGroup(resolvedEntityGroup.settings.ID, keys);
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const payload = {friendly_name: resolvedEntityDevice.name, group: resolvedEntityGroup.name};
this.mqtt.publish(
'bridge/log',
stringify({type: `device_group_remove`, message: payload}),
);
}
} else { // remove_all
logger.info(`Removing '${resolvedEntityDevice.name}' from all groups`);
await resolvedEntityDevice.endpoint.removeFromAllGroups();
for (const settingsGroup of settings.getGroups()) {
settings.removeDeviceFromGroup(settingsGroup.ID, keys);
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const payload = {friendly_name: resolvedEntityDevice.name};
this.mqtt.publish(
'bridge/log',
stringify({type: `device_group_remove_all`, message: payload}),
);
}
}
}
} catch (e) {
error = `Failed to ${type} from group (${e.message})`;
logger.debug(e.stack);
}
}
if (!triggeredViaLegacyApi) {
const response = utils.getResponse(message, responseData, error);
await this.mqtt.publish(`bridge/response/group/members/${type}`, stringify(response));
}
if (error) {
logger.error(error);
} else {
this.eventBus.emit('groupMembersChanged',
{group: resolvedEntityGroup, action: type, endpoint: resolvedEntityDevice.endpoint,
skipDisableReporting: skipDisableReporting});
}
}
}
module.exports = Groups;
+386
View File
@@ -0,0 +1,386 @@
import * as settings from '../util/settings';
import logger from '../util/logger';
import utils from '../util/utils';
import stringify from 'json-stable-stringify-without-jsonify';
import equals from 'fast-deep-equal/es6';
import bind from 'bind-decorator';
import Extension from './extension';
import Device from '../model/device';
import Group from '../model/group';
const topicRegex =
new RegExp(`^${settings.get().mqtt.base_topic}/bridge/request/group/members/(remove|add|remove_all)$`);
const legacyTopicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/group/(.+)/(remove|add|remove_all)$`);
const legacyTopicRegexRemoveAll = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/group/remove_all$`);
const stateProperties: {[s: string]: (value: string, exposes: zhc.DefinitionExpose[]) => boolean} = {
'state': () => true,
'brightness': (value, exposes) =>
!!exposes.find((e) => e.type === 'light' && e.features.find((f) => f.name === 'brightness')),
'color_temp': (value, exposes) =>
!!exposes.find((e) => e.type === 'light' && e.features.find((f) => f.name === 'color_temp')),
'color': (value, exposes) =>
!!exposes.find((e) => e.type === 'light' &&
e.features.find((f) => f.name === 'color_xy' || f.name === 'color_hs')),
'color_mode': (value, exposes) =>
!!exposes.find((e) => e.type === 'light' && (
(e.features.find((f) => f.name === `color_${value}`)) ||
(value === 'color_temp' && e.features.find((f) => f.name === 'color_temp')) )),
};
interface ParsedMQTTMessage {
type: 'remove' | 'add' | 'remove_all', resolvedEntityGroup: Group, resolvedEntityDevice: Device,
error: string, groupKey: string, deviceKey: string, triggeredViaLegacyApi: boolean,
skipDisableReporting: boolean, resolvedEntityEndpoint: zh.Endpoint,
}
export default class Groups extends Extension {
private legacyApi = settings.get().advanced.legacy_api;
private lastOptimisticState: {[s: string]: KeyValue} = {};
override async start(): Promise<void> {
this.eventBus.onStateChange(this, this.onStateChange);
this.eventBus.onMQTTMessage(this, this.onMQTTMessage);
await this.syncGroupsWithSettings();
}
private async syncGroupsWithSettings(): Promise<void> {
const settingsGroups = settings.getGroups();
const zigbeeGroups = this.zigbee.groups();
const addRemoveFromGroup = async (action: 'add' | 'remove', deviceName: string,
groupName: string | number, endpoint: zh.Endpoint, group: Group): Promise<void> => {
try {
logger.info(`${action === 'add' ? 'Adding' : 'Removing'} '${deviceName}' to group '${groupName}'`);
if (action === 'remove') {
await endpoint.removeFromGroup(group.zh);
} else {
await endpoint.addToGroup(group.zh);
}
} catch (error) {
logger.error(`Failed to ${action} '${deviceName}' from '${groupName}'`);
logger.debug(error.stack);
}
};
for (const settingGroup of settingsGroups) {
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;
if (!entity) logger.error(`Cannot find '${d}' of group '${settingGroup.friendly_name}'`);
return {'endpoint': entity?.endpoint(parsed.endpoint), 'name': entity?.name};
}).filter((e) => e.endpoint != null);
// In settings but not in zigbee
for (const entity of settingsEndpoint) {
if (!zigbeeGroup.zh.hasMember(entity.endpoint)) {
addRemoveFromGroup('add', entity.name, settingGroup.friendly_name, entity.endpoint, zigbeeGroup);
}
}
// In zigbee but not in settings
for (const endpoint of zigbeeGroup.zh.members) {
if (!settingsEndpoint.find((e) => e.endpoint === endpoint)) {
const deviceName = settings.getDevice(endpoint.getDevice().ieeeAddr).friendly_name;
addRemoveFromGroup('remove', deviceName, settingGroup.friendly_name, endpoint, zigbeeGroup);
}
}
}
for (const zigbeeGroup of zigbeeGroups) {
if (!settingsGroups.find((g) => g.ID === zigbeeGroup.ID)) {
for (const endpoint of zigbeeGroup.zh.members) {
const deviceName = settings.getDevice(endpoint.getDevice().ieeeAddr).friendly_name;
addRemoveFromGroup('remove', deviceName, zigbeeGroup.ID, endpoint, zigbeeGroup);
}
}
}
}
@bind async onStateChange(data: eventdata.StateChange): Promise<void> {
const reason = 'groupOptimistic';
if (data.reason === reason || data.reason === 'publishCached') {
return;
}
const payload: KeyValue = {};
let endpointName: string = null;
for (let [prop, value] of Object.entries(data.update)) {
const endpointNameMatch = utils.endpointNames.find((n) => prop.endsWith(`_${n}`));
if (endpointNameMatch) {
prop = prop.substring(0, prop.length - endpointNameMatch.length - 1);
endpointName = endpointNameMatch;
}
if (prop in stateProperties) {
payload[prop] = value;
}
}
if (Object.keys(payload).length) {
const entity = data.entity;
const groups = this.zigbee.groups().filter((g) => {
return g.options && (!g.options.hasOwnProperty('optimistic') || g.options.optimistic);
});
if (entity instanceof Device) {
for (const group of groups) {
if (group.zh.hasMember(entity.endpoint(endpointName)) &&
!equals(this.lastOptimisticState[group.ID], payload) &&
this.shouldPublishPayloadForGroup(group, payload)) {
this.lastOptimisticState[group.ID] = payload;
await this.publishEntityState(group, payload, reason);
}
}
} else {
// Invalidate the last optimistic group state when group state is changed directly.
delete this.lastOptimisticState[entity.ID];
const groupsToPublish: Set<Group> = new Set();
for (const member of entity.zh.members) {
const device = this.zigbee.resolveEntity(member.getDevice()) as Device;
if (device.options.disabled) continue;
const exposes = device.exposes();
const memberPayload: KeyValue = {};
Object.keys(payload).forEach((key) => {
if (stateProperties[key](payload[key], exposes)) {
memberPayload[key] = payload[key];
}
});
const endpointName = device.endpointName(member);
if (endpointName) {
Object.keys(memberPayload).forEach((key) => {
memberPayload[`${key}_${endpointName}`] = memberPayload[key];
delete memberPayload[key];
});
}
await this.publishEntityState(device, memberPayload, reason);
for (const zigbeeGroup of groups) {
if (zigbeeGroup.zh.hasMember(member) &&
this.shouldPublishPayloadForGroup(zigbeeGroup, payload)) {
groupsToPublish.add(zigbeeGroup);
}
}
}
groupsToPublish.delete(entity);
for (const group of groupsToPublish) {
await this.publishEntityState(group, payload, reason);
}
}
}
}
private shouldPublishPayloadForGroup(group: Group, payload: KeyValue): boolean {
if (group.options.off_state === 'last_member_state') return true;
if (!payload || payload.state !== 'OFF') return true;
if (this.areAllMembersOff(group)) return true;
return false;
}
private areAllMembersOff(group: Group): boolean {
for (const member of group.zh.members) {
const device = this.zigbee.resolveEntity(member.getDevice());
if (this.state.exists(device)) {
const state = this.state.get(device);
if (state.state === 'ON') {
return false;
}
}
}
return true;
}
private parseMQTTMessage(data: eventdata.MQTTMessage): ParsedMQTTMessage {
let type: 'remove' | 'add' | 'remove_all' = null;
let resolvedEntityGroup: Group = null;
let resolvedEntityDevice: Device = null;
let resolvedEntityEndpoint: zh.Endpoint = null;
let error: string = null;
let groupKey: string = null;
let deviceKey: string = null;
let triggeredViaLegacyApi = false;
let skipDisableReporting = false;
/* istanbul ignore else */
const topicRegexMatch = data.topic.match(topicRegex);
const legacyTopicRegexRemoveAllMatch = data.topic.match(legacyTopicRegexRemoveAll);
const legacyTopicRegexMatch = data.topic.match(legacyTopicRegex);
if (this.legacyApi && (legacyTopicRegexMatch || legacyTopicRegexRemoveAllMatch)) {
triggeredViaLegacyApi = true;
if (legacyTopicRegexMatch) {
resolvedEntityGroup = this.zigbee.resolveEntity(legacyTopicRegexMatch[1]) as Group;
type = legacyTopicRegexMatch[2] as 'remove' | 'remove_all' | 'add';
if (!resolvedEntityGroup || !(resolvedEntityGroup instanceof Group)) {
logger.error(`Group '${legacyTopicRegexMatch[1]}' does not exist`);
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const payload = {friendly_name: data.message,
group: legacyTopicRegexMatch[1], error: 'group doesn\'t exists'};
this.mqtt.publish(
'bridge/log',
stringify({type: `device_group_${type}_failed`, message: payload}),
);
}
return null;
}
} else {
type = 'remove_all';
}
const parsedEntity = utils.parseEntityID(data.message);
resolvedEntityDevice = this.zigbee.resolveEntity(parsedEntity.ID) as Device;
if (!resolvedEntityDevice || !(resolvedEntityDevice instanceof Device)) {
logger.error(`Device '${data.message}' does not exist`);
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const payload = {
friendly_name: data.message, group: legacyTopicRegexMatch[1], error: 'entity doesn\'t exists',
};
this.mqtt.publish(
'bridge/log',
stringify({type: `device_group_${type}_failed`, message: payload}),
);
}
return null;
}
resolvedEntityEndpoint = resolvedEntityDevice.endpoint(parsedEntity.endpoint);
} else if (topicRegexMatch) {
type = topicRegexMatch[1] as 'remove' | 'add' | 'remove_all';
const message = JSON.parse(data.message);
deviceKey = message.device;
skipDisableReporting = 'skip_disable_reporting' in message ? message.skip_disable_reporting : false;
if (type !== 'remove_all') {
groupKey = message.group;
resolvedEntityGroup = this.zigbee.resolveEntity(message.group) as Group;
if (!resolvedEntityGroup || !(resolvedEntityGroup instanceof Group)) {
error = `Group '${message.group}' does not exist`;
}
}
const parsed = utils.parseEntityID(message.device);
resolvedEntityDevice = this.zigbee.resolveEntity(parsed.ID) as Device;
if (!error && (!resolvedEntityDevice || !(resolvedEntityDevice instanceof Device))) {
error = `Device '${message.device}' does not exist`;
}
if (!error) {
resolvedEntityEndpoint = resolvedEntityDevice.endpoint(parsed.endpoint);
}
}
return {
resolvedEntityGroup, resolvedEntityDevice, type, error, groupKey, deviceKey,
triggeredViaLegacyApi, skipDisableReporting, resolvedEntityEndpoint,
};
}
@bind private async onMQTTMessage(data: eventdata.MQTTMessage): Promise<void> {
const parsed = this.parseMQTTMessage(data);
if (!parsed || !parsed.type) return;
let {
resolvedEntityGroup, resolvedEntityDevice, type, error, triggeredViaLegacyApi,
groupKey, deviceKey, skipDisableReporting, resolvedEntityEndpoint,
} = parsed;
const message = utils.parseJSON(data.message, data.message);
let changedGroups: Group[] = [];
const responseData: KeyValue = {device: deviceKey};
if (groupKey) {
responseData.group = groupKey;
}
if (!error) {
try {
const keys = [
`${resolvedEntityDevice.ieeeAddr}/${resolvedEntityEndpoint.ID}`,
`${resolvedEntityDevice.name}/${resolvedEntityEndpoint.ID}`,
];
const endpointNameLocal = resolvedEntityDevice.endpointName(resolvedEntityEndpoint);
if (endpointNameLocal) {
keys.push(`${resolvedEntityDevice.ieeeAddr}/${endpointNameLocal}`);
keys.push(`${resolvedEntityDevice.name}/${endpointNameLocal}`);
}
if (!endpointNameLocal) {
keys.push(resolvedEntityDevice.name);
keys.push(resolvedEntityDevice.ieeeAddr);
}
if (type === 'add') {
logger.info(`Adding '${resolvedEntityDevice.name}' to '${resolvedEntityGroup.name}'`);
await resolvedEntityEndpoint.addToGroup(resolvedEntityGroup.zh);
settings.addDeviceToGroup(resolvedEntityGroup.ID.toString(), keys);
changedGroups.push(resolvedEntityGroup);
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const payload = {friendly_name: resolvedEntityDevice.name, group: resolvedEntityGroup.name};
this.mqtt.publish(
'bridge/log',
stringify({type: `device_group_add`, message: payload}),
);
}
} else if (type === 'remove') {
logger.info(`Removing '${resolvedEntityDevice.name}' from '${resolvedEntityGroup.name}'`);
await resolvedEntityEndpoint.removeFromGroup(resolvedEntityGroup.zh);
settings.removeDeviceFromGroup(resolvedEntityGroup.ID.toString(), keys);
changedGroups.push(resolvedEntityGroup);
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const payload = {friendly_name: resolvedEntityDevice.name, group: resolvedEntityGroup.name};
this.mqtt.publish(
'bridge/log',
stringify({type: `device_group_remove`, message: payload}),
);
}
} else { // remove_all
logger.info(`Removing '${resolvedEntityDevice.name}' from all groups`);
changedGroups = this.zigbee.groups().filter((g) => g.zh.members.includes(resolvedEntityEndpoint));
await resolvedEntityEndpoint.removeFromAllGroups();
for (const settingsGroup of settings.getGroups()) {
settings.removeDeviceFromGroup(settingsGroup.ID.toString(), keys);
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const payload = {friendly_name: resolvedEntityDevice.name};
this.mqtt.publish(
'bridge/log',
stringify({type: `device_group_remove_all`, message: payload}),
);
}
}
}
} catch (e) {
error = `Failed to ${type} from group (${e.message})`;
logger.debug(e.stack);
}
}
if (!triggeredViaLegacyApi) {
const response = utils.getResponse(message, responseData, error);
await this.mqtt.publish(`bridge/response/group/members/${type}`, stringify(response));
}
if (error) {
logger.error(error);
} else {
for (const group of changedGroups) {
this.eventBus.emitGroupMembersChanged({
group, action: type, endpoint: resolvedEntityEndpoint, skipDisableReporting});
}
}
}
}
File diff suppressed because it is too large Load Diff
-227
View File
@@ -1,227 +0,0 @@
const logger = require('../../util/logger');
const settings = require('../../util/settings');
const utils = require('../../util/utils');
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const Extension = require('../extension');
const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/(.*)/availability`);
// Pingable end devices, some end devices should be pinged
// e.g. E11-G13 https://github.com/Koenkk/zigbee2mqtt/issues/775#issuecomment-453683846
const pingableEndDevices = [
zigbeeHerdsmanConverters.devices.find((d) => d.model === 'E11-G13'),
zigbeeHerdsmanConverters.devices.find((d) => d.model === 'E11-N1EA'),
zigbeeHerdsmanConverters.devices.find((d) => d.model === '53170161'),
];
const Hours25 = 1000 * 60 * 60 * 25;
const AvailabilityLagRatio = 0.1;
function timeoutLag(timeout, ratio) {
const lag = timeout * ratio;
return Math.floor(Math.random() * Math.floor(lag));
}
/**
* This extensions pings devices to check if they are online.
*/
class AvailabilityLegacy extends Extension {
constructor(zigbee, mqtt, state, publishEntityState, eventBus) {
super(zigbee, mqtt, state, publishEntityState, eventBus);
this.availability_timeout = settings.get().advanced.availability_timeout;
this.timers = {};
this.state = {};
this.eventBus.on('deviceRemoved', (data) => this.onDeviceRemoved(data.resolvedEntity), this.constructor.name);
this.eventBus.on('deviceRenamed', (data) => this.onDeviceRenamed(data), this.constructor.name);
this.blocklist = settings.get().advanced.availability_blocklist
.concat(settings.get().advanced.availability_blacklist)
.map((e) => settings.getEntity(e).ID);
this.passlist = settings.get().advanced.availability_passlist
.concat(settings.get().advanced.availability_whitelist)
.map((e) => settings.getEntity(e).ID);
}
onDeviceRenamed(data) {
this.mqtt.publish(`${data.from}/availability`, null, {retain: true, qos: 0});
}
onDeviceRemoved(resolvedEntity) {
this.mqtt.publish(`${resolvedEntity.name}/availability`, null, {retain: true, qos: 0});
delete this.state[resolvedEntity.device.ieeeAddr];
clearTimeout(this.timers[resolvedEntity.device.ieeeAddr]);
}
inPasslistOrNotInBlocklist(device) {
const ieeeAddr = device.ieeeAddr;
const deviceSettings = settings.getDevice(ieeeAddr);
const name = deviceSettings && deviceSettings.friendlyName;
// Passlist is not empty and device is in it, enable availability
if (this.passlist.length > 0) {
return this.passlist.includes(ieeeAddr) || (name && this.passlist.includes(name));
}
// Device is on blocklist, disable availability
if (this.blocklist.includes(ieeeAddr) || (name && this.blocklist.includes(name))) {
return false;
}
return true;
}
isPingable(device) {
if (pingableEndDevices.find((d) => d.hasOwnProperty('zigbeeModel') && d.zigbeeModel.includes(device.modelID))) {
return true;
}
// Device is a mains powered router
return device.type === 'Router' && device.powerSource !== 'Battery';
}
onMQTTConnected() {
for (const device of this.zigbee.getClientsLegacy()) {
// Mark all devices as online on start
const ieeeAddr = device.ieeeAddr;
this.publishAvailability(device, this.state.hasOwnProperty(ieeeAddr) ? this.state[ieeeAddr] : true, true);
if (this.inPasslistOrNotInBlocklist(device)) {
if (this.isPingable(device)) {
this.setTimerPingable(device);
} else {
this.timers[ieeeAddr] = setInterval(() => {
this.handleIntervalNotPingable(device);
}, utils.seconds(300));
}
}
}
}
async onMQTTMessage(topic, message) {
// Clear topics for non-existing devices
const match = topic.match(topicRegex);
if (match && (!this.zigbee.resolveEntityLegacy(match[1]) ||
this.zigbee.resolveEntityLegacy(match[1]).name !== match[1])) {
this.mqtt.publish(`${match[1]}/availability`, null, {retain: true, qos: 0});
}
}
async handleIntervalPingable(device) {
// When a device is already unavailable, log the ping failed on 'debug' instead of 'error'.
const resolvedEntity = this.zigbee.resolveEntityLegacy(device.ieeeAddr);
if (!resolvedEntity) {
logger.debug(`Stop pinging '${device.ieeeAddr}', device is not known anymore`);
return;
}
const level = this.state.hasOwnProperty(device.ieeeAddr) && !this.state[device.ieeeAddr] ? 'debug' : 'error';
try {
await device.ping();
this.publishAvailability(device, true);
logger.debug(`Successfully pinged '${resolvedEntity.name}'`);
} catch (error) {
this.publishAvailability(device, false);
logger[level](`Failed to ping '${resolvedEntity.name}'`);
} finally {
this.setTimerPingable(device);
}
}
async handleIntervalNotPingable(device) {
const resolvedEntity = this.zigbee.resolveEntityLegacy(device.ieeeAddr);
if (!resolvedEntity || !device.lastSeen) {
return;
}
const ago = Date.now() - resolvedEntity.device.lastSeen;
logger.debug(`Non-pingable device '${resolvedEntity.name}' was last seen '${ago / 1000}' seconds ago.`);
if (ago > Hours25) {
this.publishAvailability(device, false);
}
}
setTimerPingable(device) {
const timeout = this.availability_timeout + timeoutLag(this.availability_timeout, AvailabilityLagRatio);
clearTimeout(this.timers[device.ieeeAddr]);
this.timers[device.ieeeAddr] = setTimeout(async () => {
await this.handleIntervalPingable(device);
}, utils.seconds(timeout));
}
async stop() {
super.stop();
for (const timer of Object.values(this.timers)) {
clearTimeout(timer);
}
this.zigbee.getClientsLegacy().forEach((device) => this.publishAvailability(device, false));
}
async onReconnect(device) {
const resolvedEntity = this.zigbee.resolveEntityLegacy(device);
if (resolvedEntity && resolvedEntity.definition) {
try {
for (const key of ['state', 'brightness', 'color', 'color_temp']) {
const converter = resolvedEntity.definition.toZigbee.find((tz) => tz.key.includes(key));
if (converter) {
await converter.convertGet(device.endpoints[0], key, {});
}
}
} catch (error) {
logger.error(`Failed to read state of '${resolvedEntity.name}' after reconnect`);
}
}
}
publishAvailability(device, available, force=false) {
const ieeeAddr = device.ieeeAddr;
if (this.state.hasOwnProperty(ieeeAddr) && !this.state[ieeeAddr] && available) {
this.onReconnect(device);
}
const resolvedEntity = this.zigbee.resolveEntityLegacy(device);
const topic = `${resolvedEntity.name}/availability`;
const payload = available ? 'online' : 'offline';
if (this.state[ieeeAddr] !== available || force) {
this.state[ieeeAddr] = available;
this.mqtt.publish(topic, payload, {retain: true, qos: 0});
}
}
onZigbeeEvent(type, data, resolvedEntity) {
const device = data.device;
if (!device) {
return;
}
if (this.inPasslistOrNotInBlocklist(device)) {
this.publishAvailability(data.device, true);
if (this.isPingable(device)) {
// When a zigbee message from a device is received we know the device is still alive.
// => reset the timer.
this.setTimerPingable(device);
const online = this.state.hasOwnProperty(device.ieeeAddr) && this.state[device.ieeeAddr];
if (online && type === 'deviceAnnounce' && !utils.isIkeaTradfriDevice(device)) {
/**
* In case the device is powered off AND on within the availability timeout,
* zigbee2qmtt does not detect the device as offline (device is still marked online).
* When a device is turned on again the state could be out of sync.
* https://github.com/Koenkk/zigbee2mqtt/issues/1383#issuecomment-489412168
* deviceAnnounce is typically send when a device comes online.
*
* This isn't needed for TRADFRI devices as they already send the state themself.
*/
this.onReconnect(device);
}
}
}
}
}
module.exports = AvailabilityLegacy;
@@ -1,41 +1,21 @@
const settings = require('../../util/settings');
const logger = require('../../util/logger');
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const utils = require('../../util/utils');
const assert = require('assert');
const Extension = require('../extension');
const stringify = require('json-stable-stringify-without-jsonify');
import * as settings from '../../util/settings';
import logger from '../../util/logger';
import zigbeeHerdsmanConverters from 'zigbee-herdsman-converters';
import utils from '../../util/utils';
import assert from 'assert';
import Extension from '../extension';
import stringify from 'json-stable-stringify-without-jsonify';
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'];
class BridgeLegacy extends Extension {
constructor(zigbee, mqtt, state, publishEntityState, eventBus) {
super(zigbee, mqtt, state, publishEntityState, eventBus);
export default class BridgeLegacy extends Extension {
private lastJoinedDeviceName: string = null;
private supportedOptions: {[s: string]: (topic: string, message: string) => Promise<void> | void};
// Bind functions
this.permitJoin = this.permitJoin.bind(this);
this.lastSeen = this.lastSeen.bind(this);
this.elapsed = this.elapsed.bind(this);
this.reset = this.reset.bind(this);
this.logLevel = this.logLevel.bind(this);
this.devices = this.devices.bind(this);
this.groups = this.groups.bind(this);
this.rename = this.rename.bind(this);
this.renameLast = this.renameLast.bind(this);
this.remove = this.remove.bind(this);
this.forceRemove = this.forceRemove.bind(this);
this.ban = this.ban.bind(this);
this.deviceOptions = this.deviceOptions.bind(this);
this.addGroup = this.addGroup.bind(this);
this.removeGroup = this.removeGroup.bind(this);
this.whitelist = this.whitelist.bind(this);
this.touchlinkFactoryReset = this.touchlinkFactoryReset.bind(this);
this.lastJoinedDeviceName = null;
// Set supported options
override async start(): Promise<void> {
this.supportedOptions = {
'permit_join': this.permitJoin,
'last_seen': this.lastSeen,
@@ -57,24 +37,32 @@ class BridgeLegacy extends Extension {
'whitelist': this.whitelist,
'touchlink/factory_reset': this.touchlinkFactoryReset,
};
this.eventBus.onDeviceJoined(this, (data) => this.onZigbeeEvent_('deviceJoined', data, data.device));
this.eventBus.onDeviceInterview(this, (data) => this.onZigbeeEvent_('deviceInterview', data, data.device));
this.eventBus.onDeviceAnnounce(this, (data) => this.onZigbeeEvent_('deviceAnnounce', data, data.device));
this.eventBus.onDeviceLeave(this, (data) => this.onZigbeeEvent_('deviceLeave', data, null));
this.eventBus.onMQTTMessage(this, this.onMQTTMessage);
await this.publish();
}
whitelist(topic, message) {
@bind whitelist(topic: string, message: string): void {
try {
const entity = settings.getEntity(message);
const entity = settings.getDevice(message);
assert(entity, `Entity '${message}' does not exist`);
settings.whitelistDevice(entity.ID);
logger.info(`Whitelisted '${entity.friendlyName}'`);
settings.addDeviceToPasslist(entity.ID.toString());
logger.info(`Whitelisted '${entity.friendly_name}'`);
this.mqtt.publish(
'bridge/log',
stringify({type: 'device_whitelisted', message: {friendly_name: entity.friendlyName}}),
stringify({type: 'device_whitelisted', message: {friendly_name: entity.friendly_name}}),
);
} catch (error) {
logger.error(`Failed to whitelist '${message}' '${error}'`);
}
}
deviceOptions(topic, message) {
@bind deviceOptions(topic: string, message: string): void {
let json = null;
try {
json = JSON.parse(message);
@@ -88,18 +76,18 @@ class BridgeLegacy extends Extension {
return;
}
const entity = settings.getEntity(json.friendly_name);
const entity = settings.getDevice(json.friendly_name);
assert(entity, `Entity '${json.friendly_name}' does not exist`);
settings.changeEntityOptions(entity.ID, json.options);
settings.changeEntityOptions(entity.ID.toString(), json.options);
logger.info(`Changed device specific options of '${json.friendly_name}' (${stringify(json.options)})`);
}
async permitJoin(topic, message) {
await this.zigbee.permitJoinLegacy(message.toLowerCase() === 'true');
@bind async permitJoin(topic: string, message: string): Promise<void> {
await this.zigbee.permitJoin(message.toLowerCase() === 'true');
this.publish();
}
async reset(topic, message) {
@bind async reset(): Promise<void> {
try {
await this.zigbee.reset('soft');
logger.info('Soft resetted ZNP');
@@ -108,7 +96,7 @@ class BridgeLegacy extends Extension {
}
}
lastSeen(topic, message) {
@bind lastSeen(topic: string, message: string): void {
const allowed = ['disable', 'ISO_8601', 'epoch', 'ISO_8601_local'];
if (!allowed.includes(message)) {
logger.error(`${message} is not an allowed value, possible: ${allowed}`);
@@ -119,7 +107,7 @@ class BridgeLegacy extends Extension {
logger.info(`Set last_seen to ${message}`);
}
elapsed(topic, message) {
@bind elapsed(topic: string, message: string): void {
const allowed = ['true', 'false'];
if (!allowed.includes(message)) {
logger.error(`${message} is not an allowed value, possible: ${allowed}`);
@@ -130,8 +118,8 @@ class BridgeLegacy extends Extension {
logger.info(`Set elapsed to ${message}`);
}
logLevel(topic, message) {
const level = message.toLowerCase();
@bind logLevel(topic: string, message: string): void {
const level = message.toLowerCase() as 'error' | 'warn' | 'info' | 'debug';
if (allowedLogLevels.includes(level)) {
logger.info(`Switching log level to '${level}'`);
logger.setLevel(level);
@@ -142,30 +130,29 @@ class BridgeLegacy extends Extension {
this.publish();
}
async devices(topic, message) {
@bind async devices(topic: string): Promise<void> {
const coordinator = await this.zigbee.getCoordinatorVersion();
const devices = this.zigbee.getDevicesLegacy().map((device) => {
const payload = {
const devices = this.zigbee.devices().map((device) => {
const payload: KeyValue = {
ieeeAddr: device.ieeeAddr,
type: device.type,
networkAddress: device.networkAddress,
type: device.zh.type,
networkAddress: device.zh.networkAddress,
};
if (device.type !== 'Coordinator') {
const definition = zigbeeHerdsmanConverters.findByDevice(device);
const friendlyDevice = settings.getDevice(device.ieeeAddr);
payload.model = definition ? definition.model : device.modelID;
if (device.zh.type !== 'Coordinator') {
const definition = zigbeeHerdsmanConverters.findByDevice(device.zh);
payload.model = definition ? definition.model : device.zh.modelID;
payload.vendor = definition ? definition.vendor : '-';
payload.description = definition ? definition.description : '-';
payload.friendly_name = friendlyDevice ? friendlyDevice.friendly_name : device.ieeeAddr;
payload.manufacturerID = device.manufacturerID;
payload.manufacturerName = device.manufacturerName;
payload.powerSource = device.powerSource;
payload.modelID = device.modelID;
payload.hardwareVersion = device.hardwareVersion;
payload.softwareBuildID = device.softwareBuildID;
payload.dateCode = device.dateCode;
payload.lastSeen = device.lastSeen;
payload.friendly_name = device.name;
payload.manufacturerID = device.zh.manufacturerID;
payload.manufacturerName = device.zh.manufacturerName;
payload.powerSource = device.zh.powerSource;
payload.modelID = device.zh.modelID;
payload.hardwareVersion = device.zh.hardwareVersion;
payload.softwareBuildID = device.zh.softwareBuildID;
payload.dateCode = device.zh.dateCode;
payload.lastSeen = device.zh.lastSeen;
} else {
payload.friendly_name = 'Coordinator';
payload.softwareBuildID = coordinator.type;
@@ -185,17 +172,15 @@ class BridgeLegacy extends Extension {
}
}
groups(topic, message) {
@bind groups(): void {
const payload = settings.getGroups().map((g) => {
const group = {...g};
delete group.friendlyName;
return group;
return {...g, ID: Number(g.ID)};
});
this.mqtt.publish('bridge/log', stringify({type: 'groups', message: payload}));
}
rename(topic, message) {
@bind rename(topic: string, message: string): void {
const invalid =
`Invalid rename message format expected {"old": "friendly_name", "new": "new_name"} got ${message}`;
@@ -216,7 +201,7 @@ class BridgeLegacy extends Extension {
this._renameInternal(json.old, json.new);
}
renameLast(topic, message) {
@bind renameLast(topic: string, message: string): void {
if (!this.lastJoinedDeviceName) {
logger.error(`Cannot rename last joined device, no device has joined during this session`);
return;
@@ -225,15 +210,15 @@ class BridgeLegacy extends Extension {
this._renameInternal(this.lastJoinedDeviceName, message);
}
_renameInternal(from, to) {
_renameInternal(from: string, to: string): void {
try {
const isGroup = settings.getGroup(from) !== null;
settings.changeFriendlyName(from, to);
logger.info(`Successfully renamed - ${from} to ${to} `);
const entity = this.zigbee.resolveEntityLegacy(to);
const eventData = isGroup ? {group: entity.group} : {device: entity.device, from, to};
eventData.homeAssisantRename = false;
this.eventBus.emit(`${isGroup ? 'group' : 'device'}Renamed`, eventData);
const entity = this.zigbee.resolveEntity(to);
if (entity.isDevice()) {
this.eventBus.emitEntityRenamed({homeAssisantRename: false, from, to, entity});
}
this.mqtt.publish(
'bridge/log',
@@ -244,7 +229,7 @@ class BridgeLegacy extends Extension {
}
}
addGroup(topic, message) {
@bind addGroup(topic: string, message: string): void {
let id = null;
let name = null;
try {
@@ -268,20 +253,20 @@ class BridgeLegacy extends Extension {
}
const group = settings.addGroup(name, id);
this.zigbee.createGroupLegacy(group.ID);
this.zigbee.createGroup(group.ID);
this.mqtt.publish('bridge/log', stringify({type: `group_added`, message: name}));
logger.info(`Added group '${name}'`);
}
removeGroup(topic, message) {
@bind removeGroup(topic: string, message: string): void {
const name = message;
const entity = this.zigbee.resolveEntityLegacy(message);
assert(entity && entity.type === 'group', `Group '${message}' does not exist`);
const entity = this.zigbee.resolveEntity(message) as Group;
assert(entity && entity.isGroup(), `Group '${message}' does not exist`);
if (topic.includes('force')) {
entity.group.removeFromDatabase();
entity.zh.removeFromDatabase();
} else {
entity.group.removeFromNetwork();
entity.zh.removeFromNetwork();
}
settings.removeGroup(message);
@@ -289,21 +274,21 @@ class BridgeLegacy extends Extension {
logger.info(`Removed group '${name}'`);
}
async forceRemove(topic, message) {
@bind async forceRemove(topic: string, message: string): Promise<void> {
await this.removeForceRemoveOrBan('force_remove', message);
}
async remove(topic, message) {
@bind async remove(topic: string, message: string): Promise<void> {
await this.removeForceRemoveOrBan('remove', message);
}
async ban(topic, message) {
@bind async ban(topic: string, message: string): Promise<void> {
await this.removeForceRemoveOrBan('ban', message);
}
async removeForceRemoveOrBan(action, message) {
const entity = this.zigbee.resolveEntityLegacy(message.trim());
const lookup = {
@bind async removeForceRemoveOrBan(action: string, message: string): Promise<void> {
const entity = this.zigbee.resolveEntity(message.trim()) as Device;
const lookup: KeyValue = {
ban: ['banned', 'Banning', 'ban'],
force_remove: ['force_removed', 'Force removing', 'force remove'],
remove: ['removed', 'Removing', 'remove'],
@@ -316,63 +301,63 @@ class BridgeLegacy extends Extension {
return;
}
const cleanup = () => {
const ieeeAddr = entity.ieeeAddr;
const name = entity.name;
const cleanup = (): void => {
// Fire event
this.eventBus.emit('deviceRemoved', {resolvedEntity: entity});
this.eventBus.emitDeviceRemoved({ieeeAddr, name});
// Remove from configuration.yaml
settings.removeDevice(entity.settings.ID);
settings.removeDevice(entity.ieeeAddr);
// Remove from state
this.state.remove(entity.settings.ID);
this.state.remove(ieeeAddr);
logger.info(`Successfully ${lookup[action][0]} ${entity.settings.friendlyName}`);
logger.info(`Successfully ${lookup[action][0]} ${entity.name}`);
this.mqtt.publish('bridge/log', stringify({type: `device_${lookup[action][0]}`, message}));
};
try {
logger.info(`${lookup[action][1]} '${entity.settings.friendlyName}'`);
logger.info(`${lookup[action][1]} '${entity.name}'`);
if (action === 'force_remove') {
await entity.device.removeFromDatabase();
await entity.zh.removeFromDatabase();
} else {
await entity.device.removeFromNetwork();
await entity.zh.removeFromNetwork();
}
cleanup();
} catch (error) {
logger.error(`Failed to ${lookup[action][2]} ${entity.settings.friendlyName} (${error})`);
logger.error(`Failed to ${lookup[action][2]} ${entity.name} (${error})`);
// eslint-disable-next-line
logger.error(`See https://www.zigbee2mqtt.io/information/mqtt_topics_and_message_structure.html#zigbee2mqttbridgeconfigremove for more info`);
logger.error(`See https://www.zigbee2mqtt.io/guide/usage/mqtt_topics_and_messages.html#zigbee2mqtt-bridge-request for more info`);
this.mqtt.publish('bridge/log', stringify({type: `device_${lookup[action][0]}_failed`, message}));
}
if (action === 'ban') {
settings.banDevice(entity.settings.ID);
settings.blockDevice(ieeeAddr);
}
}
async onMQTTConnected() {
await this.publish();
}
async onMQTTMessage(topic, message) {
@bind async onMQTTMessage(data: eventdata.MQTTMessage): Promise<void> {
const {topic, message} = data;
if (!topic.match(configRegex)) {
return false;
return;
}
const option = topic.match(configRegex)[1];
if (!this.supportedOptions.hasOwnProperty(option)) {
return false;
return;
}
await this.supportedOptions[option](topic, message);
return true;
return;
}
async publish() {
async publish(): Promise<void> {
const info = await utils.getZigbee2MQTTVersion();
const coordinator = await this.zigbee.getCoordinatorVersion();
const topic = `bridge/config`;
@@ -388,7 +373,7 @@ class BridgeLegacy extends Extension {
await this.mqtt.publish(topic, stringify(payload), {retain: true, qos: 0});
}
onZigbeeEvent(type, data, resolvedEntity) {
onZigbeeEvent_(type: string, data: KeyValue, resolvedEntity: Device): void {
if (type === 'deviceJoined' && resolvedEntity) {
this.lastJoinedDeviceName = resolvedEntity.name;
}
@@ -436,7 +421,7 @@ class BridgeLegacy extends Extension {
} else {
/* istanbul ignore else */
if (type === 'deviceLeave') {
const name = resolvedEntity ? resolvedEntity.name : data.ieeeAddr;
const name = data.ieeeAddr;
const meta = {friendly_name: name};
this.mqtt.publish(
'bridge/log',
@@ -446,7 +431,7 @@ class BridgeLegacy extends Extension {
}
}
async touchlinkFactoryReset() {
@bind async touchlinkFactoryReset(): Promise<void> {
logger.info('Starting touchlink factory reset...');
this.mqtt.publish(
'bridge/log',
@@ -469,5 +454,3 @@ class BridgeLegacy extends Extension {
}
}
}
module.exports = BridgeLegacy;
@@ -1,50 +0,0 @@
/* istanbul ignore file */
const settings = require('../../util/settings');
const logger = require('../../util/logger');
const Extension = require('../extension');
const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/device/(.+)/get_group_membership$`);
class DeviceGroupMembership extends Extension {
async onMQTTMessage(topic, message) {
const match = topic.match(topicRegex);
if (!match) {
return null;
}
const entity = this.zigbee.resolveEntityLegacy(match[1]);
if (!entity || entity.type !== 'device') {
logger.error(`Device '${match[1]}' does not exist`);
return;
}
const response = await entity.endpoint.command(
`genGroups`, 'getMembership', {groupcount: 0, grouplist: []}, {}, true,
);
if (!response) {
logger.warn(`Couldn't get group membership of ${entity.device.ieeeAddr}`);
return;
}
let {grouplist, capacity} = response;
grouplist = grouplist.map((gid) => {
const g = settings.getGroup(gid);
return g ? g.friendlyName : gid;
});
const msgGroupList = `${entity.device.ieeeAddr} is in groups [${grouplist}]`;
let msgCapacity;
if (capacity === 254) {
msgCapacity = 'it can be a part of at least 1 more group';
} else {
msgCapacity = `its remaining group capacity is ${capacity === 255 ? 'unknown' : capacity}`;
}
logger.info(`${msgGroupList} and ${msgCapacity}`);
this.publishEntityState(entity.device.ieeeAddr, {group_list: grouplist, group_capacity: capacity});
}
}
module.exports = DeviceGroupMembership;
@@ -0,0 +1,56 @@
/* 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';
const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/device/(.+)/get_group_membership$`);
export default class DeviceGroupMembership extends Extension {
override async start(): Promise<void> {
this.eventBus.onMQTTMessage(this, this.onMQTTMessage);
}
@bind async onMQTTMessage(data: eventdata.MQTTMessage): Promise<void> {
const match = data.topic.match(topicRegex);
if (!match) {
return null;
}
const parsed = utils.parseEntityID(match[1]);
const device = this.zigbee.resolveEntity(parsed.ID) as Device;
if (!device || !(device instanceof Device)) {
logger.error(`Device '${match[1]}' does not exist`);
return;
}
const endpoint = device.endpoint(parsed.endpoint);
const response = await endpoint.command(
`genGroups`, 'getMembership', {groupcount: 0, grouplist: []}, {},
);
if (!response) {
logger.warn(`Couldn't get group membership of ${device.ieeeAddr}`);
return;
}
let {grouplist, capacity} = response;
grouplist = grouplist.map((gid: string) => {
const g = settings.getGroup(gid);
return g ? g.friendly_name : gid;
});
const msgGroupList = `${device.ieeeAddr} is in groups [${grouplist}]`;
let msgCapacity;
if (capacity === 254) {
msgCapacity = 'it can be a part of at least 1 more group';
} else {
msgCapacity = `its remaining group capacity is ${capacity === 255 ? 'unknown' : capacity}`;
}
logger.info(`${msgGroupList} and ${msgCapacity}`);
this.publishEntityState(device, {group_list: grouplist, group_capacity: capacity});
}
}
@@ -1,37 +1,40 @@
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const logger = require('../../util/logger');
const settings = require('../../util/settings');
const ZNLDP12LM = zigbeeHerdsmanConverters.devices.find((d) => d.model === 'ZNLDP12LM');
const utils = require('../../util/utils');
const Extension = require('../extension');
import zigbeeHerdsmanConverters from 'zigbee-herdsman-converters';
import logger from '../../util/logger';
import * as settings from '../../util/settings';
import Extension from '../extension';
const defaultConfiguration = {
minimumReportInterval: 3, maximumReportInterval: 300, reportableChange: 1,
};
const ZNLDP12LM = zigbeeHerdsmanConverters.definitions.find((d) => d.model === 'ZNLDP12LM');
const devicesNotSupportingReporting = [
zigbeeHerdsmanConverters.devices.find((d) => d.model === 'CC2530.ROUTER'),
zigbeeHerdsmanConverters.devices.find((d) => d.model === 'BASICZBR3'),
zigbeeHerdsmanConverters.devices.find((d) => d.model === 'ZM-CSW032-D'),
zigbeeHerdsmanConverters.devices.find((d) => d.model === 'TS0001'),
zigbeeHerdsmanConverters.devices.find((d) => d.model === 'TS0115'),
zigbeeHerdsmanConverters.definitions.find((d) => d.model === 'CC2530.ROUTER'),
zigbeeHerdsmanConverters.definitions.find((d) => d.model === 'BASICZBR3'),
zigbeeHerdsmanConverters.definitions.find((d) => d.model === 'ZM-CSW032-D'),
zigbeeHerdsmanConverters.definitions.find((d) => d.model === 'TS0001'),
zigbeeHerdsmanConverters.definitions.find((d) => d.model === 'TS0115'),
];
const reportKey = 1;
const getColorCapabilities = async (endpoint) => {
const getColorCapabilities = async (endpoint: zh.Endpoint): Promise<{colorTemperature: boolean, colorXY: boolean}> => {
if (endpoint.getClusterAttributeValue('lightingColorCtrl', 'colorCapabilities') === undefined) {
await endpoint.read('lightingColorCtrl', ['colorCapabilities']);
}
const value = endpoint.getClusterAttributeValue('lightingColorCtrl', 'colorCapabilities');
const value = endpoint.getClusterAttributeValue('lightingColorCtrl', 'colorCapabilities') as number;
return {
colorTemperature: (value & 1<<4) > 0,
colorXY: (value & 1<<3) > 0,
};
};
const clusters = {
const clusters: {[s: string]:
{attribute: string, minimumReportInterval: number, maximumReportInterval: number, reportableChange: number
condition?: (endpoint: zh.Endpoint) => Promise<boolean>}[]} =
{
'genOnOff': [
{attribute: 'onOff', ...defaultConfiguration, minimumReportInterval: 0, reportableChange: 0},
],
@@ -41,15 +44,15 @@ const clusters = {
'lightingColorCtrl': [
{
attribute: 'colorTemperature', ...defaultConfiguration,
condition: async (endpoint) => (await getColorCapabilities(endpoint)).colorTemperature,
condition: async (endpoint): Promise<boolean> => (await getColorCapabilities(endpoint)).colorTemperature,
},
{
attribute: 'currentX', ...defaultConfiguration,
condition: async (endpoint) => (await getColorCapabilities(endpoint)).colorXY,
condition: async (endpoint): Promise<boolean> => (await getColorCapabilities(endpoint)).colorXY,
},
{
attribute: 'currentY', ...defaultConfiguration,
condition: async (endpoint) => (await getColorCapabilities(endpoint)).colorXY,
condition: async (endpoint): Promise<boolean> => (await getColorCapabilities(endpoint)).colorXY,
},
],
'closuresWindowCovering': [
@@ -58,15 +61,12 @@ const clusters = {
],
};
class Report extends Extension {
constructor(zigbee, mqtt, state, publishEntityState, eventBus) {
super(zigbee, mqtt, state, publishEntityState, eventBus);
this.queue = new Set();
this.failed = new Set();
this.enabled = settings.get().advanced.report;
}
export default class Report extends Extension {
private queue: Set<string> = new Set();
private failed: Set<string> = new Set();
private enabled = settings.get().advanced.report;
shouldIgnoreClusterForDevice(cluster, definition) {
shouldIgnoreClusterForDevice(cluster: string, definition: zhc.Definition): boolean {
if (definition === ZNLDP12LM && cluster === 'closuresWindowCovering') {
// Device announces it but doesn't support it
// https://github.com/Koenkk/zigbee2mqtt/issues/2611
@@ -76,9 +76,7 @@ class Report extends Extension {
return false;
}
async setupReporting(resolvedEntity) {
const {device, definition} = resolvedEntity;
async setupReporting(device: Device): Promise<void> {
if (this.queue.has(device.ieeeAddr) || this.failed.has(device.ieeeAddr)) return;
this.queue.add(device.ieeeAddr);
@@ -86,9 +84,10 @@ class Report extends Extension {
const term2 = this.enabled ? 'setup' : 'disabled';
try {
for (const ep of device.endpoints) {
for (const ep of device.zh.endpoints) {
for (const [cluster, configuration] of Object.entries(clusters)) {
if (ep.supportsInputCluster(cluster) && !this.shouldIgnoreClusterForDevice(cluster, definition)) {
if (ep.supportsInputCluster(cluster) &&
!this.shouldIgnoreClusterForDevice(cluster, device.definition)) {
logger.debug(`${term1} reporting for '${device.ieeeAddr}' - ${ep.ID} - ${cluster}`);
const items = [];
@@ -102,8 +101,8 @@ class Report extends Extension {
}
this.enabled ?
await ep.bind(cluster, this.coordinatorEndpoint) :
await ep.unbind(cluster, this.coordinatorEndpoint);
await ep.bind(cluster, this.zigbee.firstCoordinatorEndpoint()) :
await ep.unbind(cluster, this.zigbee.firstCoordinatorEndpoint());
await ep.configureReporting(cluster, items);
logger.info(
@@ -114,13 +113,13 @@ class Report extends Extension {
}
if (this.enabled) {
device.meta.reporting = reportKey;
device.zh.meta.reporting = reportKey;
} else {
delete device.meta.reporting;
this.eventBus.emit('reportingDisabled', {device});
delete device.zh.meta.reporting;
this.eventBus.emitReconfigure({device});
}
this.eventBus.emit(`devicesChanged`);
this.eventBus.emitDevicesChanged();
} catch (error) {
logger.error(
`Failed to ${term1.toLowerCase()} reporting for '${device.ieeeAddr}' - ${error.stack}`,
@@ -129,15 +128,13 @@ class Report extends Extension {
this.failed.add(device.ieeeAddr);
}
device.save();
device.zh.save();
this.queue.delete(device.ieeeAddr);
}
shouldSetupReporting(resolvedEntity, messageType) {
if (!resolvedEntity || !resolvedEntity.device || !resolvedEntity.definition ||
messageType === 'deviceLeave') return false;
shouldSetupReporting(device: Device, messageType: string): boolean {
if (!device || !device.zh || !device.definition) return false;
const {device, definition} = resolvedEntity;
// Handle messages of type endDeviceAnnce and devIncoming.
// This message is typically send when a device comes online after being powered off
// Ikea TRADFRI tend to forget their reporting after powered off.
@@ -146,48 +143,52 @@ class Report extends Extension {
// else reconfigure is done in zigbee-herdsman-converters ikea.js/bulbOnEvent
// configuredReportings are saved since Zigbee2MQTT 1.17.0
// https://github.com/Koenkk/zigbee2mqtt/issues/966
if (this.enabled && messageType === 'deviceAnnounce' && utils.isIkeaTradfriDevice(device) &&
device.endpoints.filter((e) => e.configuredReportings.length === 0).length === device.endpoints.length) {
if (this.enabled && messageType === 'deviceAnnounce' && device.isIkeaTradfri() &&
device.zh.endpoints.filter((e) => e.configuredReportings.length === 0).length ===
device.zh.endpoints.length) {
return true;
}
// These do not support reproting.
// https://github.com/Koenkk/zigbee-herdsman/issues/110
const philipsIgnoreSw = ['5.127.1.26581', '5.130.1.30000'];
if (device.manufacturerName === 'Philips' && philipsIgnoreSw.includes(device.softwareBuildID)) return false;
if (device.zh.manufacturerName === 'Philips' &&
philipsIgnoreSw.includes(device.zh.softwareBuildID)) return false;
if (resolvedEntity.device.interviewing === true) return false;
if (device.type !== 'Router' || device.powerSource === 'Battery') return false;
if (device.zh.interviewing === true) return false;
if (device.zh.type !== 'Router' || device.zh.powerSource === 'Battery') return false;
// Gledopto devices don't support reporting.
if (devicesNotSupportingReporting.includes(definition) || definition.vendor === 'Gledopto') return false;
if (devicesNotSupportingReporting.includes(device.definition) ||
device.definition.vendor === 'Gledopto') return false;
if (this.enabled && device.meta.hasOwnProperty('reporting') && device.meta.reporting === reportKey) {
if (this.enabled && device.zh.meta.hasOwnProperty('reporting') &&
device.zh.meta.reporting === reportKey) {
return false;
}
if (!this.enabled && !device.meta.hasOwnProperty('reporting')) {
if (!this.enabled && !device.zh.meta.hasOwnProperty('reporting')) {
return false;
}
return true;
}
async onZigbeeStarted() {
this.coordinatorEndpoint = this.zigbee.getDevicesByTypeLegacy('Coordinator')[0].getEndpoint(1);
for (const device of this.zigbee.getClientsLegacy()) {
const resolvedEntity = this.zigbee.resolveEntityLegacy(device);
if (this.shouldSetupReporting(resolvedEntity, null)) {
await this.setupReporting(resolvedEntity);
override async start(): Promise<void> {
for (const device of this.zigbee.devices(false)) {
if (this.shouldSetupReporting(device, null)) {
await this.setupReporting(device);
}
}
this.eventBus.onDeviceAnnounce(this, (data) => this.onZigbeeEvent_('deviceAnnounce', data.device));
this.eventBus.onDeviceMessage(this, (data) => this.onZigbeeEvent_('dummy', data.device));
this.eventBus.onDeviceJoined(this, (data) => this.onZigbeeEvent_('dummy', data.device));
this.eventBus.onDeviceNetworkAddressChanged(this, (data) => this.onZigbeeEvent_('dummy', data.device));
}
async onZigbeeEvent(type, data, resolvedEntity) {
if (this.shouldSetupReporting(resolvedEntity, type)) {
await this.setupReporting(resolvedEntity);
async onZigbeeEvent_(type: string, device: Device): Promise<void> {
if (this.shouldSetupReporting(device, type)) {
await this.setupReporting(device);
}
}
}
module.exports = Report;
@@ -1,33 +1,35 @@
/* istanbul ignore file */
// DEPRECATED
const settings = require('../../util/settings');
const logger = require('../../util/logger');
const utils = require('../../util/utils');
const Extension = require('../extension');
import * as settings from '../../util/settings';
import logger from '../../util/logger';
import utils from '../../util/utils';
import Extension from '../extension';
/**
* This extensions soft resets the ZNP after a certain timeout.
*/
class SoftReset extends Extension {
constructor(zigbee, mqtt, state, publishEntityState, eventBus) {
super(zigbee, mqtt, state, publishEntityState, eventBus);
this.timer = null;
this.timeout = utils.seconds(settings.get().advanced.soft_reset_timeout);
}
export default class SoftReset extends Extension {
private timer: NodeJS.Timer = null;
private timeout = utils.seconds(settings.get().advanced.soft_reset_timeout);
onZigbeeStarted() {
override async start(): Promise<void> {
logger.debug(`Soft reset timeout set to ${this.timeout / 1000} seconds`);
this.resetTimer();
this.eventBus.onDeviceMessage(this, () => this.resetTimer());
this.eventBus.onDeviceAnnounce(this, () => this.resetTimer());
this.eventBus.onDeviceNetworkAddressChanged(this, () => this.resetTimer());
this.eventBus.onDeviceJoined(this, () => this.resetTimer());
this.eventBus.onDeviceInterview(this, () => this.resetTimer());
}
clearTimer() {
private clearTimer(): void {
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
}
resetTimer() {
private resetTimer(): void {
if (this.timeout === 0) {
return;
}
@@ -36,7 +38,7 @@ class SoftReset extends Extension {
this.timer = setTimeout(() => this.handleTimeout(), this.timeout);
}
async handleTimeout() {
private async handleTimeout(): Promise<void> {
logger.warn('Soft reset timeout triggered');
try {
@@ -57,10 +59,4 @@ class SoftReset extends Extension {
this.resetTimer();
}
onZigbeeEvent() {
this.resetTimer();
}
}
module.exports = SoftReset;
+19 -21
View File
@@ -1,14 +1,13 @@
import * as settings from '../util/settings';
import * as utils from '../util/utils';
import utils from '../util/utils';
import logger from '../util/logger';
// @ts-ignore
import stringify from 'json-stable-stringify-without-jsonify';
import ExtensionTS from './extensionts';
import Extension from './extension';
import bind from 'bind-decorator';
interface Link {
source: {ieeeAddr: string, networkAddress: number}, target: {ieeeAddr: string, networkAddress: number},
linkquality: number, depth: number, routes: RoutingTableEntry[],
linkquality: number, depth: number, routes: zh.RoutingTableEntry[],
sourceIeeeAddr: string, targetIeeeAddr: string, sourceNwkAddr: number, lqi: number, relationship: number,
}
@@ -23,7 +22,7 @@ interface Topology {
/**
* This extension creates a network map
*/
class NetworkMap extends ExtensionTS {
export default class NetworkMap extends Extension {
private legacyApi = settings.get().advanced.legacy_api;
private legacyTopic = `${settings.get().mqtt.base_topic}/bridge/networkmap`;
private legacyTopicRoutes = `${settings.get().mqtt.base_topic}/bridge/networkmap/routes`;
@@ -31,7 +30,7 @@ class NetworkMap extends ExtensionTS {
private supportedFormats: {[s: string]: (topology: Topology) => KeyValue | string};
override async start(): Promise<void> {
this.eventBus.onMQTTMessage(this, this.onMQTTMessage_);
this.eventBus.onMQTTMessage(this, this.onMQTTMessage);
this.supportedFormats = {
'raw': this.raw,
'graphviz': this.graphviz,
@@ -39,7 +38,7 @@ class NetworkMap extends ExtensionTS {
};
}
@bind async onMQTTMessage_(data: EventMQTTMessage): Promise<void> {
@bind async onMQTTMessage(data: eventdata.MQTTMessage): Promise<void> {
/* istanbul ignore else */
if (this.legacyApi) {
if ((data.topic === this.legacyTopic || data.topic === this.legacyTopicRoutes) &&
@@ -177,7 +176,7 @@ class NetworkMap extends ExtensionTS {
// Add the device model
if (node.type !== 'Coordinator') {
text.push(`---`);
const definition = this.zigbee.resolveEntityLegacy(node.ieeeAddr).definition;
const definition = (this.zigbee.resolveEntity(node.ieeeAddr) as Device).definition;
if (definition) {
text.push(`${definition.vendor} ${definition.description} (${definition.model})`);
} else {
@@ -215,12 +214,12 @@ class NetworkMap extends ExtensionTS {
async networkScan(includeRoutes: boolean): Promise<Topology> {
logger.info(`Starting network scan (includeRoutes '${includeRoutes}')`);
const devices = this.zigbee.getDevices().filter((d) => d.type !== 'GreenPower');
const lqis: Map<Device, LQI> = new Map();
const routingTables: Map<Device, RoutingTable> = new Map();
const devices = this.zigbee.devices().filter((d) => d.zh.type !== 'GreenPower' && !d.options.disabled);
const lqis: Map<Device, zh.LQI> = new Map();
const routingTables: Map<Device, zh.RoutingTable> = new Map();
const failed: Map<Device, string[]> = new Map();
for (const device of devices.filter((d) => d.type != 'EndDevice')) {
for (const device of devices.filter((d) => d.zh.type != 'EndDevice')) {
failed.set(device, []);
await utils.sleep(1); // sleep 1 second between each scan to reduce stress on network.
@@ -239,17 +238,18 @@ class NetworkMap extends ExtensionTS {
};
try {
const result = await doRequest<LQI>(async () => device.lqi());
const result = await doRequest<zh.LQI>(async () => device.zh.lqi());
lqis.set(device, result);
logger.debug(`LQI succeeded for '${device.name}'`);
} catch (error) {
failed.get(device).push('lqi');
logger.error(`Failed to execute LQI for '${device.name}'`);
logger.debug(error.stack);
}
if (includeRoutes) {
try {
const result = await doRequest(async () => device.routingTable());
const result = await doRequest(async () => device.zh.routingTable());
routingTables.set(device, result);
logger.debug(`Routing table succeeded for '${device.name}'`);
} catch (error) {
@@ -268,16 +268,16 @@ class NetworkMap extends ExtensionTS {
model: device.definition.model,
vendor: device.definition.vendor,
description: device.definition.description,
supports: Array.from(new Set((device.definition.exposes).map((e) => {
supports: Array.from(new Set((device.exposes()).map((e) => {
return e.hasOwnProperty('name') ? e.name :
`${e.type} (${e.features.map((f) => f.name).join(', ')})`;
}))).join(', '),
} : null;
topology.nodes.push({
ieeeAddr: device.ieeeAddr, friendlyName: device.name, type: device.type,
networkAddress: device.networkAddress, manufacturerName: device.manufacturerName,
modelID: device.modelID, failed: failed.get(device), lastSeen: device.lastSeen,
ieeeAddr: device.ieeeAddr, friendlyName: device.name, type: device.zh.type,
networkAddress: device.zh.networkAddress, manufacturerName: device.zh.manufacturerName,
modelID: device.zh.modelID, failed: failed.get(device), lastSeen: device.zh.lastSeen,
definition,
});
}
@@ -299,7 +299,7 @@ class NetworkMap extends ExtensionTS {
const link: Link = {
source: {ieeeAddr: neighbor.ieeeAddr, networkAddress: neighbor.networkAddress},
target: {ieeeAddr: device.ieeeAddr, networkAddress: device.networkAddress},
target: {ieeeAddr: device.ieeeAddr, networkAddress: device.zh.networkAddress},
linkquality: neighbor.linkquality, depth: neighbor.depth, routes: [],
// DEPRECATED:
sourceIeeeAddr: neighbor.ieeeAddr, targetIeeeAddr: device.ieeeAddr,
@@ -320,5 +320,3 @@ class NetworkMap extends ExtensionTS {
return topology;
}
}
module.exports = NetworkMap;
+16 -11
View File
@@ -1,14 +1,13 @@
// @ts-ignore
import zhc from 'zigbee-herdsman-converters';
import ExtensionTS from './extensionts';
import Extension from './extension';
/**
* This extension calls the zigbee-herdsman-converters onEvent.
*/
class OnEvent extends ExtensionTS {
export default class OnEvent extends Extension {
override async start(): Promise<void> {
for (const device of this.zigbee.getClients()) {
await this.callOnEvent(device, 'start', {});
for (const device of this.zigbee.devices(false)) {
this.callOnEvent(device, 'start', {});
}
this.eventBus.onDeviceMessage(this, (data) => this.callOnEvent(data.device, 'message', this.convertData(data)));
@@ -20,26 +19,32 @@ class OnEvent extends ExtensionTS {
(data) => this.callOnEvent(data.device, 'deviceAnnounce', this.convertData(data)));
this.eventBus.onDeviceNetworkAddressChanged(this,
(data) => this.callOnEvent(data.device, 'deviceNetworkAddressChanged', this.convertData(data)));
this.eventBus.onEntityOptionsChanged(this,
(data) => {
if (data.entity.isDevice()) {
this.callOnEvent(data.entity, 'deviceOptionsChanged', data)
.then(() => this.eventBus.emitDevicesChanged());
}
});
}
private convertData(data: KeyValue): KeyValue {
return {...data, device: data.device.zhDevice};
return {...data, device: data.device.zh};
}
override async stop(): Promise<void> {
super.stop();
for (const device of this.zigbee.getClients()) {
for (const device of this.zigbee.devices(false)) {
await this.callOnEvent(device, 'stop', {});
}
}
private async callOnEvent(device: Device, type: string, data: KeyValue): Promise<void> {
zhc.onEvent(type, data, device.zhDevice, device.settings);
const state = this.state.get(device);
zhc.onEvent(type, data, device.zh, device.options, state);
if (device.definition?.onEvent) {
await device.definition.onEvent(type, data, device.zhDevice, device.settings);
await device.definition.onEvent(type, data, device.zh, device.options, state);
}
}
}
module.exports = OnEvent;
+105 -69
View File
@@ -1,69 +1,108 @@
import * as settings from '../util/settings';
import logger from '../util/logger';
// @ts-ignore
import stringify from 'json-stable-stringify-without-jsonify';
import * as utils from '../util/utils';
// @ts-ignore
import tradfriOTA from 'zigbee-herdsman-converters/lib/ota/tradfri';
import ExtensionTS from './extensionts';
import utils from '../util/utils';
import * as tradfriOTA from 'zigbee-herdsman-converters/lib/ota/tradfri';
import * as zigbeeOTA from 'zigbee-herdsman-converters/lib/ota/zigbeeOTA';
import Extension from './extension';
import bind from 'bind-decorator';
import Device from '../model/device';
import dataDir from '../util/data';
import * as URI from 'uri-js';
import path from 'path';
function isValidUrl(url: string): boolean {
let parsed;
try {
parsed = URI.parse(url);
} catch (_) {
// istanbul ignore next
return false;
}
return parsed.scheme === 'http' || parsed.scheme === 'https';
}
type UpdateState = 'updating' | 'idle' | 'available';
interface UpdatePayload {
update_available?: boolean
// eslint-disable-next-line camelcase
update: {progress?: number, remaining?: number, state: UpdateState}, update_available?: boolean}
update: {
progress?: number, remaining?: number, state: UpdateState,
installed_version: number | null, latest_version: number | null
}
}
const legacyTopicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/ota_update/.+$`);
const topicRegex =
new RegExp(`^${settings.get().mqtt.base_topic}/bridge/request/device/ota_update/(update|check)`, 'i');
class OTAUpdate extends ExtensionTS {
export default class OTAUpdate extends Extension {
private inProgress = new Set();
private lastChecked: {[s: string]: number} = {};
private legacyApi = settings.get().advanced.legacy_api;
override async start(): Promise<void> {
this.eventBus.onMQTTMessage(this, this.onMQTTMessage_);
this.eventBus.onMQTTMessage(this, this.onMQTTMessage);
this.eventBus.onDeviceMessage(this, this.onZigbeeEvent);
if (settings.get().advanced.ikea_ota_use_test_url) {
if (settings.get().ota.ikea_ota_use_test_url) {
tradfriOTA.useTestURL();
}
for (const device of this.zigbee.getClients()) {
// In case Zigbee2MQTT is restared during an update, progress and remaining values are still in state.
// remove them.
// Let zigbeeOTA module know if the override index file is provided
let overrideOTAIndex = settings.get().ota.zigbee_ota_override_index_location;
if (overrideOTAIndex) {
// If the file name is not a full path, then treat it as a relative to the data directory
if (!isValidUrl(overrideOTAIndex) && !path.isAbsolute(overrideOTAIndex)) {
overrideOTAIndex = dataDir.joinPath(overrideOTAIndex);
}
zigbeeOTA.useIndexOverride(overrideOTAIndex);
}
// In order to support local firmware files we need to let zigbeeOTA know where the data directory is
zigbeeOTA.setDataDir(dataDir.getPath());
// In case Zigbee2MQTT is restared during an update, progress and remaining values are still in state.
// remove them.
for (const device of this.zigbee.devices(false)) {
this.removeProgressAndRemainingFromState(device);
}
}
private removeProgressAndRemainingFromState(device: Device): void {
this.state.removeKey(device.ieeeAddr, ['update', 'progress']);
this.state.removeKey(device.ieeeAddr, ['update', 'remaining']);
delete this.state.get(device).update?.progress;
delete this.state.get(device).update?.remaining;
}
@bind private async onZigbeeEvent(data: EventDeviceMessage): Promise<void> {
if (settings.get().ota.disable_automatic_update_check) return;
if (data.type !== 'commandQueryNextImageRequest' || !data.device.definition) return;
@bind private async onZigbeeEvent(data: eventdata.DeviceMessage): Promise<void> {
if (data.type !== 'commandQueryNextImageRequest' || !data.device.definition ||
this.inProgress.has(data.device.ieeeAddr)) return;
logger.debug(`Device '${data.device.name}' requested OTA`);
const supportsOTA = data.device.definition.hasOwnProperty('ota');
if (supportsOTA) {
const automaticOTACheckDisabled = settings.get().ota.disable_automatic_update_check;
let supportsOTA = data.device.definition.hasOwnProperty('ota');
if (supportsOTA && !automaticOTACheckDisabled) {
// When a device does a next image request, it will usually do it a few times after each other
// with only 10 - 60 seconds inbetween. It doesn't make sense to check for a new update
// each time, so this interval can be set by the user. The default is 1,440 minutes (one day).
const updateCheckInterval = settings.get().ota.update_check_interval * 1000 * 60;
const check = this.lastChecked.hasOwnProperty(data.device.ieeeAddr) ?
(Date.now() - this.lastChecked[data.device.ieeeAddr]) > updateCheckInterval : true;
if (!check || this.inProgress.has(data.device.ieeeAddr)) return;
if (!check) return;
this.lastChecked[data.device.ieeeAddr] = Date.now();
const available = await data.device.definition.ota.isUpdateAvailable(
data.device.zhDevice, logger, data.data);
const payload = this.getEntityPublishPayload(available ? 'available' : 'idle');
this.publishEntityState(data.device.ieeeAddr, payload);
let availableResult: zhc.OtaUpdateAvailableResult = null;
try {
availableResult = await data.device.definition.ota.isUpdateAvailable(data.device.zh, logger, data.data);
} catch (e) {
supportsOTA = false;
logger.debug(`Failed to check if update available for '${data.device.name}' (${e.message})`);
}
if (available) {
const payload = this.getEntityPublishPayload(data.device, availableResult ?? 'idle');
this.publishEntityState(data.device, payload);
if (availableResult?.available) {
const message = `Update available for '${data.device.name}'`;
logger.info(message);
@@ -78,48 +117,45 @@ class OTAUpdate extends ExtensionTS {
}
}
// Respond to the OTA request:
// - In case we don't support OTA: respond with NO_IMAGE_AVAILABLE (0x98) (so the client stops requesting OTAs)
// - In case we do support OTA: respond with ABORT (0x95) as we don't want to update now.
const endpoint = data.device.endpoints.find((e) => e.supportsOutputCluster('genOta'));
if (endpoint) {
// Some devices send OTA requests without defining OTA cluster as input cluster.
await endpoint.commandResponse('genOta', 'queryNextImageResponse', {status: supportsOTA ? 0x95 : 0x98});
}
// Respond to the OTA request: respond with NO_IMAGE_AVAILABLE (0x98) (so the client stops requesting OTAs)
const endpoint = data.device.zh.endpoints.find((e) => e.supportsOutputCluster('genOta')) || data.endpoint;
await endpoint.commandResponse('genOta', 'queryNextImageResponse', {status: 0x98});
logger.debug(`Responded to OTA request of '${data.device.name}' with 'NO_IMAGE_AVAILABLE'`);
}
private async readSoftwareBuildIDAndDateCode(device: Device, update: boolean):
private async readSoftwareBuildIDAndDateCode(device: Device, sendWhen: 'active' | 'immediate'):
Promise<{softwareBuildID: string, dateCode: string}> {
try {
const endpoint = device.endpoints.find((e) => e.supportsInputCluster('genBasic'));
const result = await endpoint.read('genBasic', ['dateCode', 'swBuildId']);
if (update) {
device.zhDevice.softwareBuildID = result.swBuildId;
device.zhDevice.dateCode = result.dateCode;
device.zhDevice.save();
}
const endpoint = device.zh.endpoints.find((e) => e.supportsInputCluster('genBasic'));
const result = await endpoint.read('genBasic', ['dateCode', 'swBuildId'], {sendWhen});
return {softwareBuildID: result.swBuildId, dateCode: result.dateCode};
} catch (e) {
return null;
}
}
private getEntityPublishPayload(state: UpdateState, progress: number=null, remaining: number=null): UpdatePayload {
const payload: UpdatePayload = {update: {state}};
private getEntityPublishPayload(device: Device, state: zhc.OtaUpdateAvailableResult | UpdateState,
progress: number=null, remaining: number=null): UpdatePayload {
const deviceUpdateState = this.state.get(device).update;
const payload: UpdatePayload = {update: {
state: typeof state === 'string' ? state : (state.available ? 'available' : 'idle'),
installed_version: typeof state === 'string' ?
deviceUpdateState?.installed_version : state.currentFileVersion,
latest_version: typeof state === 'string' ?
deviceUpdateState?.latest_version : state.otaFileVersion,
}};
if (progress !== null) payload.update.progress = progress;
if (remaining !== null) payload.update.remaining = Math.round(remaining);
/* istanbul ignore else */
if (this.legacyApi) {
payload.update_available = state === 'available';
payload.update_available = typeof state === 'string' ? state === 'available' : state.available;
}
return payload;
}
@bind async onMQTTMessage_(data: EventMQTTMessage): Promise<void> {
@bind async onMQTTMessage(data: eventdata.MQTTMessage): Promise<void> {
if ((!this.legacyApi || !data.topic.match(legacyTopicRegex)) && !data.topic.match(topicRegex)) {
return null;
}
@@ -164,23 +200,24 @@ class OTAUpdate extends ExtensionTS {
}
try {
const available = await device.definition.ota.isUpdateAvailable(device.zhDevice, logger);
const msg = `${available ? 'Update' : 'No update'} available for '${device.name}'`;
const availableResult = await device.definition.ota.isUpdateAvailable(device.zh, logger);
const msg = `${availableResult.available ? 'Update' : 'No update'} available for '${device.name}'`;
logger.info(msg);
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const meta = {status: available ? 'available' : 'not_available', device: device.name};
const meta = {
status: availableResult.available ? 'available' : 'not_available', device: device.name};
this.mqtt.publish(
'bridge/log',
stringify({type: `ota_update`, message: msg, meta}),
);
}
const payload = this.getEntityPublishPayload(available ? 'available' : 'idle');
this.publishEntityState(device.ieeeAddr, payload);
const payload = this.getEntityPublishPayload(device, availableResult);
this.publishEntityState(device, payload);
this.lastChecked[device.ieeeAddr] = Date.now();
responseData.updateAvailable = available;
responseData.updateAvailable = availableResult.available;
} catch (e) {
error = `Failed to check if update available for '${device.name}' (${e.message})`;
errorStack = e.stack;
@@ -216,8 +253,8 @@ class OTAUpdate extends ExtensionTS {
logger.info(msg);
const payload = this.getEntityPublishPayload('updating', progress, remaining);
this.publishEntityState(device.ieeeAddr, payload);
const payload = this.getEntityPublishPayload(device, 'updating', progress, remaining);
this.publishEntityState(device, payload);
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
@@ -226,19 +263,20 @@ class OTAUpdate extends ExtensionTS {
}
};
const from_ = await this.readSoftwareBuildIDAndDateCode(device, false);
await device.definition.ota.updateToLatest(device.zhDevice, logger, onProgress);
const to = await this.readSoftwareBuildIDAndDateCode(device, true);
const [fromS, toS] = [stringify(from_), stringify(to)];
const msg = `Finished update of '${device.name}'` +
(to ? `, from '${fromS}' to '${toS}'` : ``);
logger.info(msg);
const from_ = await this.readSoftwareBuildIDAndDateCode(device, 'immediate');
const fileVersion = await device.definition.ota.updateToLatest(device.zh, logger, onProgress);
logger.info(`Finished update of '${device.name}'`);
this.eventBus.emitReconfigure({device});
this.removeProgressAndRemainingFromState(device);
const payload = this.getEntityPublishPayload('idle');
this.publishEntityState(device.ieeeAddr, payload);
const payload = this.getEntityPublishPayload(device,
{available: false, currentFileVersion: fileVersion, otaFileVersion: fileVersion});
this.publishEntityState(device, payload);
const to = await this.readSoftwareBuildIDAndDateCode(device, 'active');
const [fromS, toS] = [stringify(from_), stringify(to)];
logger.info(`Device '${device.name}' was updated from '${fromS}' to '${toS}'`);
responseData.from = from_ ? utils.toSnakeCase(from_) : null;
responseData.to = to ? utils.toSnakeCase(to) : null;
this.eventBus.emit(`devicesChanged`);
this.eventBus.emitDevicesChanged();
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
@@ -251,8 +289,8 @@ class OTAUpdate extends ExtensionTS {
errorStack = e.stack;
this.removeProgressAndRemainingFromState(device);
const payload = this.getEntityPublishPayload('available');
this.publishEntityState(device.ieeeAddr, payload);
const payload = this.getEntityPublishPayload(device, 'available');
this.publishEntityState(device, payload);
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
@@ -277,5 +315,3 @@ class OTAUpdate extends ExtensionTS {
}
}
}
module.exports = OTAUpdate;
+69 -55
View File
@@ -1,42 +1,42 @@
import * as settings from '../util/settings';
// @ts-ignore
import zhc from 'zigbee-herdsman-converters';
import zigbeeHerdsmanConverters from 'zigbee-herdsman-converters';
import * as philips from 'zigbee-herdsman-converters/lib/philips';
import logger from '../util/logger';
import * as utils from '../util/utils';
import ExtensionTS from './extensionts';
// @ts-ignore
import utils from '../util/utils';
import Extension from './extension';
import stringify from 'json-stable-stringify-without-jsonify';
import Group from '../model/group';
import Device from '../model/device';
import bind from 'bind-decorator';
const topicRegex = new RegExp(`^(.+?)(?:/(${utils.endpointNames.join('|')}))?/(get|set)(?:/(.+))?`);
const propertyEndpointRegex = new RegExp(`^(.*)_(${utils.endpointNames.join('|')})$`);
const topicRegex = new RegExp(`^(.+?)(?:/(${utils.endpointNames.join('|')}|\\d+))?/(get|set)(?:/(.+))?`);
const propertyEndpointRegex = new RegExp(`^(.*?)_(${utils.endpointNames.join('|')})$`);
const stateValues = ['on', 'off', 'toggle', 'open', 'close', 'stop', 'lock', 'unlock'];
const sceneConverterKeys = ['scene_store', 'scene_add', 'scene_remove', 'scene_remove_all'];
// Legacy: don't provide default converters anymore, this is required by older z2m installs not saving group members
const defaultGroupConverters = [
zhc.toZigbeeConverters.light_onoff_brightness,
zhc.toZigbeeConverters.light_color_colortemp,
zhc.toZigbeeConverters.effect,
zhc.toZigbeeConverters.ignore_transition,
zhc.toZigbeeConverters.cover_position_tilt,
zhc.toZigbeeConverters.thermostat_occupied_heating_setpoint,
zhc.toZigbeeConverters.tint_scene,
zhc.toZigbeeConverters.light_brightness_move,
zhc.toZigbeeConverters.light_brightness_step,
zhc.toZigbeeConverters.light_colortemp_step,
zhc.toZigbeeConverters.light_colortemp_move,
zhc.toZigbeeConverters.light_hue_saturation_move,
zhc.toZigbeeConverters.light_hue_saturation_step,
zigbeeHerdsmanConverters.toZigbeeConverters.light_onoff_brightness,
zigbeeHerdsmanConverters.toZigbeeConverters.light_color_colortemp,
philips.tz.effect, // Support Hue effects for groups
zigbeeHerdsmanConverters.toZigbeeConverters.ignore_transition,
zigbeeHerdsmanConverters.toZigbeeConverters.cover_position_tilt,
zigbeeHerdsmanConverters.toZigbeeConverters.thermostat_occupied_heating_setpoint,
zigbeeHerdsmanConverters.toZigbeeConverters.tint_scene,
zigbeeHerdsmanConverters.toZigbeeConverters.light_brightness_move,
zigbeeHerdsmanConverters.toZigbeeConverters.light_brightness_step,
zigbeeHerdsmanConverters.toZigbeeConverters.light_colortemp_step,
zigbeeHerdsmanConverters.toZigbeeConverters.light_colortemp_move,
zigbeeHerdsmanConverters.toZigbeeConverters.light_hue_saturation_move,
zigbeeHerdsmanConverters.toZigbeeConverters.light_hue_saturation_step,
];
interface ParsedTopic {ID: string, endpoint: string, attribute: string, type: 'get' | 'set'}
class Publish extends ExtensionTS {
export default class Publish extends Extension {
async start(): Promise<void> {
this.eventBus.onMQTTMessage(this, this.onMQTTMessage_);
this.eventBus.onMQTTMessage(this, this.onMQTTMessage);
}
parseTopic(topic: string): ParsedTopic | null {
@@ -54,7 +54,7 @@ class Publish extends ExtensionTS {
return {ID: ID, endpoint: match[2], type: match[3] as 'get' | 'set', attribute: match[4]};
}
parseMessage(parsedTopic: ParsedTopic, data: EventMQTTMessage): KeyValue | null {
parseMessage(parsedTopic: ParsedTopic, data: eventdata.MQTTMessage): KeyValue | null {
if (parsedTopic.attribute) {
try {
return {[parsedTopic.attribute]: JSON.parse(data.message)};
@@ -81,8 +81,8 @@ class Publish extends ExtensionTS {
}
}
legacyRetrieveState(re: Device | Group, converter: ToZigbeeConverter, result: ToZigbeeConverterResult,
target: ZHEndpoint | ZHGroup, key: string, meta: ToZigbeeConverterGetMeta): void {
legacyRetrieveState(re: Device | Group, converter: zhc.ToZigbeeConverter, result: zhc.ToZigbeeConverterResult,
target: zh.Endpoint | zh.Group, key: string, meta: zhc.ToZigbeeConverterGetMeta): void {
// It's possible for devices to get out of sync when writing an attribute that's not reportable.
// So here we re-read the value after a specified timeout, this timeout could for example be the
// transition time of a color change or for forcing a state read for devices that don't
@@ -90,9 +90,9 @@ class Publish extends ExtensionTS {
// When reporting is requested for a device (report: true in device-specific settings) we won't
// ever issue a read here, as we assume the device will properly report changes.
// Only do this when the retrieve_state option is enabled for this device.
// retrieve_state == decprecated
// retrieve_state == deprecated
if (re instanceof Device && result && result.hasOwnProperty('readAfterWriteTime') &&
re.settings.retrieve_state
re.options.retrieve_state
) {
setTimeout(() => converter.convertGet(target, key, meta), result.readAfterWriteTime);
}
@@ -102,7 +102,7 @@ class Publish extends ExtensionTS {
/**
* Home Assistant always publishes 'state', even when e.g. only setting
* the color temperature. This would lead to 2 zigbee publishes, where the first one
* (state) is probably unecessary.
* (state) is probably unnecessary.
*/
if (settings.get().homeassistant) {
const hasColorTemp = message.hasOwnProperty('color_temp');
@@ -116,8 +116,7 @@ class Publish extends ExtensionTS {
}
}
// TODO remove trailing _
@bind async onMQTTMessage_(data: EventMQTTMessage): Promise<void> {
@bind async onMQTTMessage(data: eventdata.MQTTMessage): Promise<void> {
const parsedTopic = this.parseTopic(data.topic);
if (!parsedTopic) return;
@@ -130,28 +129,29 @@ class Publish extends ExtensionTS {
// Get entity details
const definition = re instanceof Device ? re.definition : re.membersDefinitions();
if (definition == null) {
logger.error(`Cannot publish to unsupported device '${re.name}'`);
return;
}
const target = re instanceof Group ? re.zhGroup : re.endpoint(parsedTopic.endpoint);
const target = re instanceof Group ? re.zh : re.endpoint(parsedTopic.endpoint);
if (target == null) {
logger.error(`Device '${re.name}' has no endpoint '${parsedTopic.endpoint}'`);
return;
}
const device = re instanceof Device ? re.zhDevice : null;
const entitySettings = re.settings;
const entityState = this.state.get(re.ID) || {};
const device = re instanceof Device ? re.zh : null;
const entitySettings = re.options;
const entityState = this.state.get(re);
const membersState = re instanceof Group ?
Object.fromEntries(re.membersIeeeAddr().map((e) => [e, this.state.get(e)])) : null;
let converters: ToZigbeeConverter[];
Object.fromEntries(re.zh.members.map((e) => [e.getDevice().ieeeAddr,
this.state.get(this.zigbee.resolveEntity(e.getDevice().ieeeAddr))])) : null;
let converters: zhc.ToZigbeeConverter[];
{
if (Array.isArray(definition)) {
const c = new Set(definition.map((d) => d.toZigbee).flat());
// @ts-expect-error
if (c.size == 0) converters = defaultGroupConverters;
else converters = Array.from(c);
} else {
} else if (definition) {
converters = definition.toZigbee;
} else {
converters = [zigbeeHerdsmanConverters.toZigbeeConverters.read,
zigbeeHerdsmanConverters.toZigbeeConverters.write];
}
}
@@ -167,7 +167,7 @@ class Publish extends ExtensionTS {
* Order state & brightness based on current bulb state
*
* Not all bulbs support setting the color/color_temp while it is off
* this results in inconsistant behavior between different vendors.
* this results in inconsistent behavior between different vendors.
*
* bulb on => move state & brightness to the back
* bulb off => move state & brightness to the front
@@ -177,18 +177,22 @@ class Publish extends ExtensionTS {
entries.sort((a) => (['state', 'brightness', 'brightness_percent'].includes(a[0]) ? sorter : sorter * -1));
// For each attribute call the corresponding converter
const usedConverters: {[s: number]: ToZigbeeConverter[]} = {};
const usedConverters: {[s: number]: zhc.ToZigbeeConverter[]} = {};
const toPublish: {[s: number | string]: KeyValue} = {};
const addToToPublish = (ID: number | string, payload: KeyValue): void => {
if (!(ID in toPublish)) toPublish[ID] = {};
const toPublishEntity: {[s: number | string]: Device | Group} = {};
const addToToPublish = (entity: Device | Group, payload: KeyValue): void => {
const ID = entity.ID;
if (!(ID in toPublish)) {
toPublish[ID] = {};
toPublishEntity[ID] = entity;
}
toPublish[ID] = {...toPublish[ID], ...payload};
};
for (let [key, value] of entries) {
let endpointName = parsedTopic.endpoint;
let localTarget = target;
// @ts-ignore TODO fix
let endpointOrGroupID = target.constructor.name === 'Endpoint' ? target.ID : target.groupID;
let endpointOrGroupID = utils.isEndpoint(target) ? target.ID : target.groupID;
// When the key has a endpointName included (e.g. state_right), this will override the target.
const propertyEndpointMatch = key.match(propertyEndpointRegex);
@@ -213,10 +217,16 @@ class Publish extends ExtensionTS {
}
if (!converter) {
logger.error(`No converter available for '${key}' (${message[key]})`);
logger.error(`No converter available for '${key}' (${stringify(message[key])})`);
continue;
}
// If the endpoint_name name is a number, try to map it to a friendlyName
if (!isNaN(Number(endpointName)) && re.isDevice() && utils.isEndpoint(localTarget) &&
re.endpointName(localTarget)) {
endpointName = re.endpointName(localTarget);
}
// Converter didn't return a result, skip
const meta = {endpoint_name: endpointName, options: entitySettings, message: {...message}, logger, device,
state: entityState, membersState, mapped: definition};
@@ -248,13 +258,14 @@ class Publish extends ExtensionTS {
}
// filter out attribute listed in filtered_optimistic
entitySettings.filtered_optimistic?.forEach((a) => delete msg[a]);
addToToPublish(re.ID, msg);
utils.filterProperties(entitySettings.filtered_optimistic, msg);
addToToPublish(re, msg);
}
if (result && result.membersState && optimistic) {
for (const [ieeeAddr, state] of Object.entries(result.membersState)) {
addToToPublish(ieeeAddr, state);
addToToPublish(this.zigbee.resolveEntity(ieeeAddr), state);
}
}
@@ -279,11 +290,14 @@ class Publish extends ExtensionTS {
for (const [ID, payload] of Object.entries(toPublish)) {
if (Object.keys(payload).length != 0) {
this.publishEntityState(ID, payload);
this.publishEntityState(toPublishEntity[ID], payload);
}
}
const scenesChanged = Object.values(usedConverters)
.some((cl) => cl.some((c) => c.key.some((k) => sceneConverterKeys.includes(k))));
if (scenesChanged) {
this.eventBus.emitScenesChanged();
}
}
}
// TODO_finished: : change class to export default
module.exports = Publish;
+60 -54
View File
@@ -1,53 +1,62 @@
import * as settings from '../util/settings';
import logger from '../util/logger';
import debounce from 'debounce';
import ExtensionTS from './extensionts';
// @ts-ignore
import Extension from './extension';
import stringify from 'json-stable-stringify-without-jsonify';
import bind from 'bind-decorator';
import utils from '../util/utils';
class Receive extends ExtensionTS {
type DebounceFunction = (() => void) & { clear(): void; } & { flush(): void; };
export default class Receive extends Extension {
private elapsed: {[s: string]: number} = {};
// eslint-disable-next-line
private debouncers: {[s: string]: {payload: KeyValue, publish: any}} = {}; //TODO fix type
private debouncers: {[s: string]: {payload: KeyValue, publish: DebounceFunction }} = {};
async start(): Promise<void> {
this.eventBus.onPublishEntityState(this, this.onPublishEntityState);
this.eventBus.onDeviceMessage(this, this.onDeviceMessage);
}
@bind async onPublishEntityState(data: EventPublishEntityState): Promise<void> {
@bind async onPublishEntityState(data: eventdata.PublishEntityState): Promise<void> {
/**
* Prevent that outdated properties are being published.
* In case that e.g. the state is currently held back by a debounce and a new state is published
* remove it from the to be send debounced message.
*/
if (data.ieeeAddr && this.debouncers[data.ieeeAddr] && data.stateChangeReason !== 'publishDebounce') {
if (data.entity.isDevice() && this.debouncers[data.entity.ieeeAddr] &&
data.stateChangeReason !== 'publishDebounce' && data.stateChangeReason !== 'lastSeenChanged') {
for (const key of Object.keys(data.payload)) {
delete this.debouncers[data.ieeeAddr].payload[key];
delete this.debouncers[data.entity.ieeeAddr].payload[key];
}
}
}
publishDebounce(ieeeAddr: string, payload: KeyValue, time: number, debounceIgnore: string[]): void {
if (!this.debouncers[ieeeAddr]) {
this.debouncers[ieeeAddr] = {
publishDebounce(device: Device, payload: KeyValue, time: number, debounceIgnore: string[]): void {
if (!this.debouncers[device.ieeeAddr]) {
this.debouncers[device.ieeeAddr] = {
payload: {},
publish: debounce(() => {
this.publishEntityState(ieeeAddr, this.debouncers[ieeeAddr].payload, 'publishDebounce');
this.debouncers[ieeeAddr].payload = {};
this.publishEntityState(device, this.debouncers[device.ieeeAddr].payload, 'publishDebounce');
this.debouncers[device.ieeeAddr].payload = {};
}, time * 1000),
};
}
if (this.isPayloadConflicted(payload, this.debouncers[ieeeAddr].payload, debounceIgnore)) {
if (this.isPayloadConflicted(payload, this.debouncers[device.ieeeAddr].payload, debounceIgnore)) {
// publish previous payload immediately
this.debouncers[ieeeAddr].publish.flush();
this.debouncers[device.ieeeAddr].publish.flush();
}
// extend debounced payload with current
this.debouncers[ieeeAddr].payload = {...this.debouncers[ieeeAddr].payload, ...payload};
this.debouncers[ieeeAddr].publish();
this.debouncers[device.ieeeAddr].payload = {...this.debouncers[device.ieeeAddr].payload, ...payload};
// Update state cache right away. This makes sure that during debouncing cached state is always up to date.
// ( Update right away as "lastSeenChanged" event might occur while debouncer is still active.
// And if that happens it would cause old message to be published from cache.
// By updating cache we make sure that state cache is always up-to-date.
this.state.set(device, this.debouncers[device.ieeeAddr].payload);
this.debouncers[device.ieeeAddr].publish();
}
// if debounce_ignore are specified (Array of strings)
@@ -67,15 +76,16 @@ class Receive extends ExtensionTS {
return result;
}
shouldProcess(data: EventDeviceMessage): boolean {
shouldProcess(data: eventdata.DeviceMessage): boolean {
if (!data.device.definition) {
if (data.device.interviewing) {
if (data.device.zh.interviewing) {
logger.debug(`Skipping message, definition is undefined and still interviewing`);
} else {
logger.warn(
`Received message from unsupported device with Zigbee model '${data.device.modelID}' ` +
`and manufacturer name '${data.device.manufacturerName}'`);
logger.warn(`Please see: https://www.zigbee2mqtt.io/how_tos/how_to_support_new_devices.html.`);
`Received message from unsupported device with Zigbee model '${data.device.zh.modelID}' ` +
`and manufacturer name '${data.device.zh.manufacturerName}'`);
// eslint-disable-next-line max-len
logger.warn(`Please see: https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html`);
}
return false;
@@ -84,39 +94,28 @@ class Receive extends ExtensionTS {
return true;
}
@bind onDeviceMessage(data: EventDeviceMessage): void {
@bind async onDeviceMessage(data: eventdata.DeviceMessage): Promise<void> {
/* istanbul ignore next */
if (!data.device) return;
/**
* Handling of re-transmitted Xiaomi messages.
* https://github.com/Koenkk/zigbee2mqtt/issues/1238
* https://github.com/Koenkk/zigbee2mqtt/issues/3592
*
* Some Xiaomi router devices re-transmit messages from Xiaomi end devices.
* The network address of these message is set to the one of the Xiaomi router.
* Therefore it looks like if the message came from the Xiaomi router, while in
* fact it came from the end device.
* Handling these message would result in false state updates.
* The group ID attribute of these message defines the network address of the end device.
*/
if (data.device.isXiaomiDevice() && data.device.isRouter() && data.groupID) {
logger.debug('Handling re-transmitted Xiaomi message');
data = {...data, device: this.zigbee.deviceByNetworkAddress(data.groupID)};
if (!this.shouldProcess(data)) {
utils.publishLastSeen({device: data.device, reason: 'messageEmitted'},
settings.get(), true, this.publishEntityState);
return;
}
if (!this.shouldProcess(data)) return;
const converters = data.device.definition.fromZigbee.filter((c) => {
const type = Array.isArray(c.type) ? c.type.includes(data.type) : c.type === data.type;
return c.cluster === data.cluster && type;
});
// Check if there is an available converter, genOta messages are not interesting.
const ignoreClusters: (string | number)[] = ['genOta', 'genTime', 'genBasic'];
const ignoreClusters: (string | number)[] = ['genOta', 'genTime', 'genBasic', 'genPollCtrl'];
if (converters.length == 0 && !ignoreClusters.includes(data.cluster)) {
logger.debug(`No converter available for '${data.device.definition.model}' with ` +
`cluster '${data.cluster}' and type '${data.type}' and data '${stringify(data.data)}'`);
utils.publishLastSeen({device: data.device, reason: 'messageEmitted'},
settings.get(), true, this.publishEntityState);
return;
}
@@ -136,27 +135,34 @@ class Receive extends ExtensionTS {
}
// Check if we have to debounce
if (data.device.settings.debounce) {
this.publishDebounce(data.device.ieeeAddr, payload, data.device.settings.debounce,
data.device.settings.debounce_ignore);
if (data.device.options.debounce) {
this.publishDebounce(data.device, payload, data.device.options.debounce,
data.device.options.debounce_ignore);
} else {
this.publishEntityState(data.device.ieeeAddr, payload);
this.publishEntityState(data.device, payload);
}
};
const meta = {device: data.device.zhDevice, logger, state: this.state.get(data.device.ieeeAddr)};
let payload = {};
converters.forEach((converter) => {
const converted = converter.convert(data.device.definition, data, publish, data.device.settings, meta);
if (converted) {
payload = {...payload, ...converted};
const meta = {device: data.device.zh, logger, state: this.state.get(data.device)};
let payload: KeyValue = {};
for (const converter of converters) {
try {
const converted = await converter.convert(
data.device.definition, data, publish, data.device.options, meta);
if (converted) {
payload = {...payload, ...converted};
}
} catch (error) /* istanbul ignore next */ {
logger.error(`Exception while calling fromZigbee converter: ${error.message}}`);
logger.debug(error.stack);
}
});
}
if (Object.keys(payload).length) {
publish(payload);
} else {
utils.publishLastSeen({device: data.device, reason: 'messageEmitted'},
settings.get(), true, this.publishEntityState);
}
}
}
module.exports = Receive;
+50 -42
View File
@@ -1,71 +1,79 @@
/* eslint-disable brace-style */
import * as settings from '../util/settings';
// @ts-ignore
import zhc from 'zigbee-herdsman-converters';
import zigbeeHerdsmanConverters from 'zigbee-herdsman-converters';
export default class Device {
private device: ZHDevice;
private _definition: Definition;
public zh: zh.Device;
private _definition: zhc.Definition;
private _definitionModelID: string;
get endpoints(): ZHEndpoint[] {return this.device.endpoints;}
get zhDevice(): ZHDevice {return this.device;}
get ieeeAddr(): string {return this.device.ieeeAddr;}
get ID(): string {return this.device.ieeeAddr;}
get settings(): DeviceSettings {return {...settings.get().device_options, ...settings.getDevice(this.ieeeAddr)};}
get ieeeAddr(): string {return this.zh.ieeeAddr;}
get ID(): string {return this.zh.ieeeAddr;}
get options(): DeviceOptions {return {...settings.get().device_options, ...settings.getDevice(this.ieeeAddr)};}
get name(): string {
return this.type === 'Coordinator' ? 'Coordinator' : this.settings?.friendlyName || this.ieeeAddr;}
get lastSeen(): number {return this.device.lastSeen;}
get modelID(): string {return this.device.modelID;}
get softwareBuildID(): string {return this.device.softwareBuildID;}
get dateCode(): string {return this.device.dateCode;}
get interviewCompleted(): boolean {return this.device.interviewCompleted;}
get networkAddress(): number {return this.device.networkAddress;}
get manufacturerName(): string {return this.device.manufacturerName;}
get interviewing(): boolean {return this.device.interviewing;}
get type(): 'Coordinator' | 'Router' | 'EndDevice' | 'Unknown' | 'GreenPower' {return this.device.type;}
get powerSource(): string {return this.device.powerSource;}
get definition(): Definition | undefined {
if (!this._definition && !this.device.interviewing) {
this._definition = zhc.findByDevice(this.device);
return this.zh.type === 'Coordinator' ? 'Coordinator' : this.options?.friendly_name || this.ieeeAddr;
}
get definition(): zhc.Definition {
// Some devices can change modelID, reconsider the definition in that case.
// https://github.com/Koenkk/zigbee-herdsman-converters/issues/3016
if (!this.zh.interviewing && (!this._definition || this._definitionModelID !== this.zh.modelID)) {
this._definition = zigbeeHerdsmanConverters.findByDevice(this.zh);
this._definitionModelID = this.zh.modelID;
}
return this._definition;
}
constructor(device: ZHDevice) {
this.device = device;
constructor(device: zh.Device) {
this.zh = device;
}
async ping(disableRecovery: boolean): Promise<void> {await this.device.ping(disableRecovery);}
async removeFromNetwork(): Promise<void> {await this.device.removeFromNetwork();}
exposes(): zhc.DefinitionExpose[] {
/* istanbul ignore if */
if (typeof this.definition.exposes == 'function') {
return this.definition.exposes(this.zh, this.options);
} else {
return this.definition.exposes;
}
}
endpoint(key?: string): ZHEndpoint {
let endpoint: ZHEndpoint;
if (key == null) key = 'default';
ensureInSettings(): void {
if (this.zh.type !== 'Coordinator' && !settings.getDevice(this.zh.ieeeAddr)) {
settings.addDevice(this.zh.ieeeAddr);
}
}
endpoint(key?: string | number): zh.Endpoint {
let endpoint: zh.Endpoint;
if (key == null || key == '') key = 'default';
if (!isNaN(Number(key))) {
endpoint = this.device.getEndpoint(Number(key));
endpoint = this.zh.getEndpoint(Number(key));
} else if (this.definition?.endpoint) {
const ID = this.definition?.endpoint?.(this.device)[key];
if (ID) endpoint = this.device.getEndpoint(ID);
else if (key === 'default') endpoint = this.device.endpoints[0];
const ID = this.definition?.endpoint?.(this.zh)[key];
if (ID) endpoint = this.zh.getEndpoint(ID);
else if (key === 'default') endpoint = this.zh.endpoints[0];
else return null;
} else {
/* istanbul ignore next */
if (key !== 'default') return null;
endpoint = this.device.endpoints[0];
endpoint = this.zh.endpoints[0];
}
return endpoint;
}
isXiaomiDevice(): boolean {
const xiaomiManufacturerID = [4151, 4447];
endpointName(endpoint: zh.Endpoint): string {
let name = null;
if (this.definition?.endpoint) {
name = Object.entries(this.definition?.endpoint(this.zh)).find((e) => e[1] == endpoint.ID)[0];
}
/* istanbul ignore next */
return this.zhDevice.modelID !== 'lumi.router' && xiaomiManufacturerID.includes(this.zhDevice.manufacturerID) &&
(!this.zhDevice.manufacturerName || !this.zhDevice.manufacturerName.startsWith('Trust'));
return name === 'default' ? null : name;
}
isRouter(): boolean {return this.zhDevice.type === 'Router';}
lqi(): Promise<LQI> {return this.zhDevice.lqi();}
routingTable(): Promise<RoutingTable> {return this.zhDevice.routingTable();}
isIkeaTradfri(): boolean {return this.zh.manufacturerID === 4476;}
isDevice(): this is Device {return true;}
/* istanbul ignore next */
isGroup(): this is Group {return false;}
}
+21 -14
View File
@@ -1,26 +1,33 @@
/* eslint-disable brace-style */
import * as settings from '../util/settings';
// @ts-ignore
import zhc from 'zigbee-herdsman-converters';
import zigbeeHerdsmanConverters from 'zigbee-herdsman-converters';
export default class Group {
private group: ZHGroup;
public zh: zh.Group;
private resolveDevice: (ieeeAddr: string) => Device;
get zhGroup(): ZHGroup {return this.group;}
get ID(): number {return this.group.groupID;}
get settings(): GroupSettings {return settings.getGroup(this.ID);}
get name(): string {return this.settings?.friendlyName || this.ID.toString();}
get members(): ZHEndpoint[] {return this.group.members;}
get ID(): number {return this.zh.groupID;}
get options(): GroupOptions {return {...settings.getGroup(this.ID)};}
get name(): string {return this.options?.friendly_name || this.ID.toString();}
constructor(group: ZHGroup) {
this.group = group;
constructor(group: zh.Group, resolveDevice: (ieeeAddr: string) => Device) {
this.zh = group;
this.resolveDevice = resolveDevice;
}
membersDefinitions(): Definition[] {
return this.members.map((m) => zhc.findByDevice(m.getDevice())).filter((d) => d) as Definition[];
hasMember(device: Device): boolean {
return !!device.zh.endpoints.find((e) => this.zh.members.includes(e));
}
membersIeeeAddr(): string[] {
return this.members.map((m) => m.getDevice().ieeeAddr);
membersDevices(): Device[] {
return this.zh.members.map((e) => this.resolveDevice(e.getDevice().ieeeAddr)).filter((d) => d);
}
membersDefinitions(): zhc.Definition[] {
return this.zh.members.map((m) =>
zigbeeHerdsmanConverters.findByDevice(m.getDevice())).filter((d) => d) as zhc.Definition[];
}
isDevice(): this is Device {return false;}
isGroup(): this is Group {return true;}
}
+53 -11
View File
@@ -1,19 +1,22 @@
import mqtt from 'mqtt';
import logger from './util/logger';
import * as settings from './util/settings';
import {seconds} from './util/utils';
import utils from './util/utils';
import fs from 'fs';
import bind from 'bind-decorator';
export default class MQTT {
private publishedTopics: Set<string> = new Set();
private connectionTimer: NodeJS.Timeout;
private client: mqtt.MqttClient;
private eventBus: EventBus;
private initialConnect = true;
private republishRetainedTimer: NodeJS.Timer;
private retainedMessages: {[s: string]: {payload: string, options: MQTTOptions,
skipLog: boolean, skipReceive: boolean, topic: string, base: string}} = {};
constructor(eventBus: EventBus) {
this.eventBus = eventBus;
this.onMessage = this.onMessage.bind(this);
this.onConnect = this.onConnect.bind(this);
}
async connect(): Promise<void> {
@@ -23,7 +26,7 @@ export default class MQTT {
const options: mqtt.IClientOptions = {
will: {
topic: `${settings.get().mqtt.base_topic}/bridge/state`,
payload: 'offline',
payload: utils.availabilityPayload('offline', settings.get()),
retain: settings.get().mqtt.force_disable_retain ? false : true,
qos: 1,
},
@@ -68,8 +71,11 @@ export default class MQTT {
options.rejectUnauthorized = false;
}
return new Promise((resolve) => {
return new Promise((resolve, reject) => {
this.client = mqtt.connect(mqttSettings.server, options);
// @ts-ignore https://github.com/Koenkk/zigbee2mqtt/issues/9822
this.client.stream.setMaxListeners(0);
this.eventBus.onPublishAvailability(this, () => this.publishStateOnline());
const onConnect = this.onConnect;
this.client.on('connect', async () => {
@@ -77,27 +83,48 @@ export default class MQTT {
resolve();
});
this.client.on('error', (err) => {
logger.error(`MQTT error: ${err.message}`);
reject(err);
});
this.client.on('message', this.onMessage);
});
}
private async onConnect(): Promise<void> {
@bind private async onConnect(): Promise<void> {
// Set timer at interval to check if connected to MQTT server.
clearTimeout(this.connectionTimer);
this.connectionTimer = setInterval(() => {
if (this.client.reconnecting) {
logger.error('Not connected to MQTT server!');
}
}, seconds(10));
}, utils.seconds(10));
logger.info('Connected to MQTT server');
await this.publishStateOnline();
if (!this.initialConnect) {
this.republishRetainedTimer = setTimeout(() => {
// Republish retained messages in case MQTT broker does not persist them.
// https://github.com/Koenkk/zigbee2mqtt/issues/9629
Object.values(this.retainedMessages).forEach((e) =>
this.publish(e.topic, e.payload, e.options, e.base, e.skipLog, e.skipReceive));
}, 2000);
}
this.initialConnect = false;
this.subscribe(`${settings.get().mqtt.base_topic}/#`);
await this.publish('bridge/state', 'online', {retain: true, qos: 0});
}
async publishStateOnline(): Promise<void> {
await this.publish('bridge/state', utils.availabilityPayload('online', settings.get()), {retain: true, qos: 0});
}
async disconnect(): Promise<void> {
clearTimeout(this.connectionTimer);
await this.publish('bridge/state', 'offline', {retain: true, qos: 0});
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();
}
@@ -106,18 +133,24 @@ export default class MQTT {
this.client.subscribe(topic);
}
public onMessage(topic: string, message: string): void {
@bind public onMessage(topic: string, message: string): 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}'`);
this.eventBus.emitMQTTMessage({topic, message: message + ''});
}
if (this.republishRetainedTimer && topic === `${settings.get().mqtt.base_topic}/bridge/info`) {
clearTimeout(this.republishRetainedTimer);
this.republishRetainedTimer = null;
}
}
isConnected(): boolean {
return this.client && !this.client.reconnecting;
}
async publish(topic: string, payload: string, options: {qos?: mqtt.QoS, retain?: boolean}={},
async publish(topic: string, payload: string, options: MQTTOptions={},
base=settings.get().mqtt.base_topic, skipLog=false, skipReceive=true,
): Promise<void> {
const defaultOptions: {qos: mqtt.QoS, retain: boolean} = {qos: 0, retain: false};
@@ -127,6 +160,15 @@ export default class MQTT {
this.publishedTopics.add(topic);
}
if (options.retain) {
if (payload) {
this.retainedMessages[topic] =
{payload, options, skipReceive, skipLog, topic: topic.substring(base.length + 1), base};
} else {
delete this.retainedMessages[topic];
}
}
this.eventBus.emitMQTTMessagePublished({topic, payload, options: {...defaultOptions, ...options}});
if (!this.isConnected()) {
-119
View File
@@ -1,119 +0,0 @@
const logger = require('./util/logger');
const data = require('./util/data');
const settings = require('./util/settings');
const fs = require('fs');
const objectAssignDeep = require('object-assign-deep');
const saveInterval = 1000 * 60 * 5; // 5 minutes
const dontCacheProperties = [
'^action$', '^action_.*$', '^button$', '^button_left$', '^button_right$', '^click$', '^forgotten$', '^keyerror$',
'^step_size$', '^transition_time$', '^group_list$', '^group_capacity$', '^no_occupancy_since$',
'^step_mode$', '^transition_time$', '^duration$', '^elapsed$', '^from_side$', '^to_side$',
];
class State {
constructor(eventBus) {
this.state = {};
this.file = data.joinPath('state.json');
this.timer = null;
this.eventBus = eventBus;
}
start() {
this._load();
// Save the state on every interval
this.clearTimer();
this.timer = setInterval(() => this.save(), saveInterval);
}
clearTimer() {
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
}
stop() {
this.clearTimer();
this.save();
}
_load() {
if (fs.existsSync(this.file)) {
try {
this.state = JSON.parse(fs.readFileSync(this.file, 'utf8'));
logger.debug(`Loaded state from file ${this.file}`);
} catch (e) {
logger.debug(`Failed to load state from file ${this.file} (corrupt file?)`);
}
} else {
logger.debug(`Can't load state from file ${this.file} (doesn't exist)`);
}
}
save() {
if (settings.get().advanced.cache_state_persistent) {
logger.debug(`Saving state to file ${this.file}`);
const json = JSON.stringify(this.state, null, 4);
try {
fs.writeFileSync(this.file, json, 'utf8');
} catch (e) {
logger.error(`Failed to write state to '${this.file}' (${e.message})`);
}
} else {
logger.debug(`Not saving state`);
}
}
exists(ID) {
return this.state.hasOwnProperty(ID);
}
get(ID) {
return this.state[ID];
}
set(ID, update, reason=null) {
const fromState = this.state[ID] || {};
const toState = objectAssignDeep.noMutate(fromState, update);
const result = {...toState};
for (const property of Object.keys(toState)) {
if (dontCacheProperties.find((p) => property.match(p))) {
delete toState[property];
}
}
this.state[ID] = toState;
this.eventBus.emit('stateChange', {ID, from: fromState, to: toState, reason, update});
return result;
}
removeKey(ID, path) {
if (this.exists(ID)) {
let state = this.state[ID];
for (let i = 0; i < path.length; i++) {
const key = path[i];
if (i === path.length - 1) {
delete state[key];
} else {
if (state[key]) {
state = state[key];
} else {
break;
}
}
}
}
}
remove(ID) {
if (this.exists(ID)) {
delete this.state[ID];
}
}
}
module.exports = State;
+96
View File
@@ -0,0 +1,96 @@
import logger from './util/logger';
import data from './util/data';
import * as settings from './util/settings';
import utils from './util/utils';
import fs from 'fs';
import objectAssignDeep from 'object-assign-deep';
const saveInterval = 1000 * 60 * 5; // 5 minutes
const dontCacheProperties = [
'action', 'action_.*', 'button', 'button_left', 'button_right', 'click', 'forgotten', 'keyerror',
'step_size', 'transition_time', 'group_list', 'group_capacity', 'no_occupancy_since',
'step_mode', 'transition_time', 'duration', 'elapsed', 'from_side', 'to_side',
];
class State {
private state: {[s: string | number]: KeyValue} = {};
private file = data.joinPath('state.json');
private timer: NodeJS.Timer = null;
constructor(private readonly eventBus: EventBus, private readonly zigbee: Zigbee) {
this.eventBus = eventBus;
this.zigbee = zigbee;
}
start(): void {
this.load();
// Save the state on every interval
this.timer = setInterval(() => this.save(), saveInterval);
}
stop(): void {
// Remove any invalid states (ie when the device has left the network) when the system is stopped
Object.keys(this.state)
.filter((k) => typeof k === 'string' && !this.zigbee.resolveEntity(k)) // string key = ieeeAddr
.forEach((k) => delete this.state[k]);
clearTimeout(this.timer);
this.save();
}
private load(): void {
if (fs.existsSync(this.file)) {
try {
this.state = JSON.parse(fs.readFileSync(this.file, 'utf8'));
logger.debug(`Loaded state from file ${this.file}`);
} catch (e) {
logger.debug(`Failed to load state from file ${this.file} (corrupt file?)`);
}
} else {
logger.debug(`Can't load state from file ${this.file} (doesn't exist)`);
}
}
private save(): void {
if (settings.get().advanced.cache_state_persistent) {
logger.debug(`Saving state to file ${this.file}`);
const json = JSON.stringify(this.state, null, 4);
try {
fs.writeFileSync(this.file, json, 'utf8');
} catch (e) {
logger.error(`Failed to write state to '${this.file}' (${e.message})`);
}
} else {
logger.debug(`Not saving state`);
}
}
exists(entity: Device | Group): boolean {
return this.state.hasOwnProperty(entity.ID);
}
get(entity: Group | Device): KeyValue {
return this.state[entity.ID] || {};
}
set(entity: Group | Device, update: KeyValue, reason: string=null): KeyValue {
const fromState = this.state[entity.ID] || {};
const toState = objectAssignDeep({}, fromState, update);
const newCache = {...toState};
const entityDontCacheProperties = entity.options.filtered_cache || [];
utils.filterProperties(dontCacheProperties.concat(entityDontCacheProperties), newCache);
this.state[entity.ID] = newCache;
this.eventBus.emitStateChange({entity, from: fromState, to: toState, reason, update});
return toState;
}
remove(ID: string | number): void {
delete this.state[ID];
}
}
export default State;
+3
View File
@@ -0,0 +1,3 @@
declare module 'json-stable-stringify-without-jsonify' {
export default function (obj: unknown): string;
}
+3
View File
@@ -0,0 +1,3 @@
declare module 'mkdir-recursive' {
export function mkdirSync(path: string): void;
}
+319
View File
@@ -0,0 +1,319 @@
/* eslint-disable camelcase */
import type {
Device as ZHDevice,
Group as ZHGroup,
Endpoint as ZHEndpoint,
} from 'zigbee-herdsman/dist/controller/model';
import type {
NetworkParameters as ZHNetworkParameters,
CoordinatorVersion as ZHCoordinatorVersion,
LQI as ZHLQI,
RoutingTable as ZHRoutingTable,
RoutingTableEntry as ZHRoutingTableEntry,
} from 'zigbee-herdsman/dist/adapter/tstype';
import type {
Cluster as ZHCluster,
} from 'zigbee-herdsman/dist/zcl/tstype';
import type {
FrameControl as ZHFrameControl,
} from 'zigbee-herdsman/dist/zcl';
import type * as ZHEvents from 'zigbee-herdsman/dist/controller/events';
import type TypeEventBus from 'lib/eventBus';
import type TypeMQTT from 'lib/mqtt';
import type TypeState from 'lib/state';
import type TypeZigbee from 'lib/zigbee';
import type TypeDevice from 'lib/model/device';
import type TypeGroup from 'lib/model/group';
import type TypeExtension from 'lib/extension/extension';
import type mqtt from 'mqtt';
declare global {
// Define some class types as global
type EventBus = TypeEventBus;
type MQTT = TypeMQTT;
type Zigbee = TypeZigbee;
type Group = TypeGroup;
type Device = TypeDevice;
type State = TypeState;
type Extension = TypeExtension;
// Types
interface MQTTResponse {data: KeyValue, status: 'error' | 'ok', error?: string, transaction?: string}
interface MQTTOptions {qos?: mqtt.QoS, retain?: boolean, properties?: {messageExpiryInterval: number}}
type StateChangeReason = 'publishDebounce' | 'groupOptimistic' | 'lastSeenChanged' | 'publishCached';
type PublishEntityState = (entity: Device | Group, payload: KeyValue,
stateChangeReason?: StateChangeReason) => Promise<void>;
type RecursivePartial<T> = {[P in keyof T]?: RecursivePartial<T[P]>;};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
interface KeyValue {[s: string]: any}
// zigbee-herdsman
namespace zh {
type Endpoint = ZHEndpoint;
type Device = ZHDevice;
type Group = ZHGroup;
type LQI = ZHLQI;
type RoutingTable = ZHRoutingTable;
type RoutingTableEntry = ZHRoutingTableEntry;
type CoordinatorVersion = ZHCoordinatorVersion;
type NetworkParameters = ZHNetworkParameters;
type Cluster = ZHCluster;
interface Bind {
cluster: zh.Cluster;
target: zh.Endpoint | zh.Group;
}
}
// zigbee-herdsman-converters
namespace zhc {
interface Logger {
info: (message: string) => void;
warn: (message: string) => void;
error: (message: string) => void;
debug: (message: string) => void;
}
interface ToZigbeeConverterGetMeta {message?: KeyValue, mapped?: Definition | Definition[]}
interface ToZigbeeConverterResult {state: KeyValue,
membersState: {[s: string]: KeyValue}, readAfterWriteTime?: number}
interface ToZigbeeConverter {
key: string[],
convertGet?: (entity: zh.Endpoint | zh.Group, key: string, meta: ToZigbeeConverterGetMeta) => Promise<void>
convertSet?: (entity: zh.Endpoint | zh.Group, key: string, value: KeyValue | string | number,
meta: {state: KeyValue}) => Promise<ToZigbeeConverterResult>
}
interface FromZigbeeConverter {
cluster: string,
type: string[] | string,
convert: (model: Definition, msg: KeyValue, publish: (payload: KeyValue) => void, options: KeyValue,
meta: {state: KeyValue, logger: Logger, device: zh.Device}) => Promise<KeyValue>,
}
interface DefinitionExposeFeature {name: string, endpoint?: string,
property: string, value_max?: number, value_min?: number, unit?: string,
value_off?: string, value_on?: string, value_step?: number, values: string[], access: number}
interface DefinitionExpose {
type: string, name?: string, features?: DefinitionExposeFeature[],
endpoint?: string, values?: string[], value_off?: string, value_on?: string, value_step?: number,
access: number, property: string, unit?: string,
value_min?: number, value_max?: number}
interface OtaUpdateAvailableResult {available: boolean, currentFileVersion: number, otaFileVersion: number}
interface Definition {
model: string,
zigbeeModel: string[],
endpoint?: (device: zh.Device) => {[s: string]: number}
toZigbee: ToZigbeeConverter[]
fromZigbee: FromZigbeeConverter[]
icon?: string
description: string
options: zhc.DefinitionExpose[],
vendor: string
exposes: DefinitionExpose[] | ((device: zh.Device, options: KeyValue) => DefinitionExpose[])
configure?: (device: zh.Device, coordinatorEndpoint: zh.Endpoint, logger: Logger,
options?: DeviceOptions) => Promise<void>;
onEvent?: (type: string, data: KeyValue, device: zh.Device,
settings: KeyValue, state: KeyValue) => Promise<void>;
ota?: {
isUpdateAvailable: (device: zh.Device, logger: Logger, data?: KeyValue)
=> Promise<OtaUpdateAvailableResult>;
updateToLatest: (device: zh.Device, logger: Logger,
onProgress: (progress: number, remaining: number) => void) => Promise<number>;
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type ExternalDefinition = Definition & {homeassistant: any};
}
namespace eventdata {
type EntityRenamed = { entity: Device | Group, homeAssisantRename: boolean, from: string, to: string };
type DeviceRemoved = { ieeeAddr: string, name: string };
type MQTTMessage = { topic: string, message: string };
type MQTTMessagePublished = { topic: string, payload: string, options: {retain: boolean, qos: number} };
type StateChange = {
entity: Device | Group, from: KeyValue, to: KeyValue, reason: string | null, update: KeyValue };
type PermitJoinChanged = ZHEvents.PermitJoinChangedPayload;
type LastSeenChanged = { device: Device,
reason: 'deviceAnnounce' | 'networkAddress' | 'deviceJoined' | 'messageEmitted' | 'messageNonEmitted'; };
type DeviceNetworkAddressChanged = { device: Device };
type DeviceAnnounce = { device: Device };
type DeviceInterview = { device: Device, status: 'started' | 'successful' | 'failed' };
type DeviceJoined = { device: Device };
type EntityOptionsChanged = { entity: Device | Group, from: KeyValue, to: KeyValue };
type Reconfigure = { device: Device };
type DeviceLeave = { ieeeAddr: string, name: string };
type GroupMembersChanged = {group: Group, action: 'remove' | 'add' | 'remove_all',
endpoint: zh.Endpoint, skipDisableReporting: boolean };
type PublishEntityState = {entity: Group | Device, message: KeyValue, stateChangeReason: StateChangeReason,
payload: KeyValue};
type DeviceMessage = {
type: ZHEvents.MessagePayloadType;
device: Device;
endpoint: zh.Endpoint;
linkquality: number;
groupID: number;
cluster: string | number;
data: KeyValue | Array<string | number>;
meta: {zclTransactionSequenceNumber?: number; manufacturerCode?: number; frameControl?: ZHFrameControl;};
};
}
// Settings
// eslint-disable camelcase
interface Settings {
homeassistant?: {
discovery_topic: string,
status_topic: string,
legacy_entity_attributes: boolean,
legacy_triggers: boolean,
},
permit_join?: boolean,
availability?: {
active: {timeout: number},
passive: {timeout: number}
},
external_converters: string[],
mqtt: {
base_topic: string,
include_device_information: boolean,
force_disable_retain: boolean
version?: number,
user?: string,
password?: string,
server: string,
ca?: string,
keepalive?: number,
key?: string,
cert?: string,
client_id?: string,
reject_unauthorized?: boolean,
},
serial: {
disable_led: boolean,
port?: string,
adapter?: 'deconz' | 'zstack' | 'ezsp' | 'zigate',
baudrate?: number,
rtscts?: boolean,
},
passlist: string[],
blocklist: string[],
map_options: {
graphviz: {
colors: {
fill: {
enddevice: string,
coordinator: string,
router: string,
},
font: {
coordinator: string,
router: string,
enddevice: string,
},
line: {
active: string,
inactive: string,
},
},
},
},
ota: {
update_check_interval: number,
disable_automatic_update_check: boolean,
zigbee_ota_override_index_location?: string,
ikea_ota_use_test_url?: boolean,
},
frontend?: {
auth_token?: string,
host?: string,
port?: number,
url?: string,
ssl_cert?: string,
ssl_key?: string,
},
devices?: {[s: string]: DeviceOptions},
groups?: {[s: string]: GroupOptions},
device_options: KeyValue,
advanced: {
legacy_api: boolean,
legacy_availability_payload: boolean,
log_rotation: boolean,
log_symlink_current: boolean,
log_output: ('console' | 'file' | 'syslog')[],
log_directory: string,
log_file: string,
log_level: 'debug' | 'info' | 'error' | 'warn',
log_syslog: KeyValue,
pan_id: number | 'GENERATE',
ext_pan_id: number[],
channel: number,
adapter_concurrent: number | null,
adapter_delay: number | null,
cache_state: boolean,
cache_state_persistent: boolean,
cache_state_send_on_startup: boolean,
last_seen: 'disable' | 'ISO_8601' | 'ISO_8601_local' | 'epoch',
elapsed: boolean,
network_key: number[] | 'GENERATE',
timestamp_format: string,
output: 'json' | 'attribute' | 'attribute_and_json',
transmit_power?: number,
// Everything below is deprecated
availability_timeout?: number,
availability_blocklist?: string[],
availability_passlist?: string[],
availability_blacklist?: string[],
availability_whitelist?: string[],
soft_reset_timeout: number,
report: boolean,
},
}
interface DeviceOptions {
ID?: string,
disabled?: boolean,
retention?: number,
availability?: boolean | {timeout: number},
optimistic?: boolean,
retrieve_state?: boolean,
debounce?: number,
debounce_ignore?: string[],
filtered_attributes?: string[],
filtered_cache?: string[],
filtered_optimistic?: string[],
icon?: string,
homeassistant?: KeyValue,
legacy?: boolean,
friendly_name: string,
description?: string,
qos?: 0 | 1 | 2,
}
interface GroupOptions {
devices?: string[],
ID?: number,
optimistic?: boolean,
off_state?: 'all_members_off' | 'last_member_state'
filtered_attributes?: string[],
filtered_cache?: string[],
filtered_optimistic?: string[],
retrieve_state?: boolean,
homeassistant?: KeyValue,
friendly_name: string,
description?: string,
qos?: 0 | 1 | 2,
}
}
-279
View File
@@ -1,279 +0,0 @@
import type {
Device as ZZHDevice,
Group as ZZHGroup,
Endpoint as ZZHEndpoint,
} from 'zigbee-herdsman/dist/controller/model';
import {
NetworkParameters as ZHNetworkParameters,
CoordinatorVersion as ZHCoordinatorVersion,
LQI as ZHLQI,
RoutingTable as ZHRoutingTable,
RoutingTableEntry as ZHRoutingTableEntry,
} from 'zigbee-herdsman/dist/adapter/tstype';
import * as D from 'lib/model/device';
import * as Z from 'lib/zigbee';
import * as E from 'lib/eventBus';
import * as M from 'lib/mqtt';
// TODO: check all
declare global {
type RecursivePartial<T> = {
[P in keyof T]?: RecursivePartial<T[P]>;
};
type EventBus = E.default;
type MQTT = M.default;
type Zigbee = Z.default;
type Device = D.default;
type ZHEndpoint = ZZHEndpoint;
type ZHDevice = ZZHDevice;
type LQI = ZHLQI;
type RoutingTable = ZHRoutingTable;
type RoutingTableEntry = ZHRoutingTableEntry;
type ZHGroup = ZZHGroup;
type CoordinatorVersion = ZHCoordinatorVersion;
type NetworkParameters = ZHNetworkParameters;
type ZigbeeEventType = 'deviceLeave' | 'deviceAnnounce';
interface ZigbeeEventData {
ieeeAddr: string;
}
/* eslint-disable */
// Controller
interface KeyValue {
[s: string]: any,
}
interface Settings {
homeassistant?: boolean,
devices?: {[s: string]: {friendly_name: string, retention?: number}},
groups?: {[s: string]: {friendly_name: string, devices?: string[]}},
passlist: string[],
blocklist: string[],
whitelist: string[],
ban: string[],
availability?: boolean | {
active?: {timeout?: number},
passive?: {timeout?: number}
},
permit_join: boolean,
frontend?: {
auth_token?: string,
host?: string,
port?: number,
},
mqtt: {
base_topic: string,
include_device_information: boolean,
force_disable_retain: boolean
version?: number,
user?: string,
password?: string,
server: string,
ca?: string,
keepalive?: number,
key?: string,
cert?: string,
client_id?: string,
reject_unauthorized?: boolean,
},
serial: {
disable_led?: boolean,
port?: string,
adapter?: 'deconz' | 'zstack' | 'ezsp' | 'zigate'
},
device_options: {[s: string]: unknown},
map_options: {
graphviz: {
colors: {
fill: {
enddevice: string,
coordinator: string,
router: string,
},
font: {
coordinator: string,
router: string,
enddevice: string,
},
line: {
active: string,
inactive: string,
},
},
},
},
experimental: {
output: 'json' | 'attribute' | 'attribute_and_json',
availability_new?: boolean,
transmit_power?: number,
},
advanced: {
legacy_api: boolean,
log_rotation: boolean,
log_symlink_current: boolean,
log_output: ('console' | 'file')[],
log_directory: string,
log_file: string,
log_level: 'debug' | 'info' | 'error' | 'warn',
log_syslog: {},
soft_reset_timeout: number,
pan_id: number | 'GENERATE',
ext_pan_id: number[],
channel: number,
adapter_concurrent: number | null,
adapter_delay: number | null,
availability_timeout: number,
availability_blocklist: string[],
availability_passlist: string[],
availability_blacklist: string[],
availability_whitelist: string[],
cache_state: boolean,
cache_state_persistent: boolean,
cache_state_send_on_startup: boolean,
last_seen: 'disable' | 'ISO_8601' | 'ISO_8601_local' | 'epoch',
elapsed: boolean,
network_key: number[] | 'GENERATE',
report: boolean,
homeassistant_discovery_topic: string,
homeassistant_status_topic: string,
homeassistant_legacy_entity_attributes: boolean,
homeassistant_legacy_triggers: boolean,
timestamp_format: string,
baudrate?: number,
rtscts?: boolean,
ikea_ota_use_test_url?: boolean,
},
ota: {
update_check_interval: number,
disable_automatic_update_check: boolean
},
external_converters: string[],
}
interface DeviceSettings {
friendlyName: string,
ID: string,
retention?: number,
availability?: boolean | {timeout: number},
optimistic?: boolean,
retrieve_state?: boolean,
debounce?: number,
debounce_ignore?: string[],
filtered_optimistic?: string[],
icon?: string,
homeassistant?: KeyValue,
legacy?: boolean,
}
interface GroupSettings {
friendlyName: string,
devices: string[],
ID: number,
optimistic?: boolean,
filtered_optimistic?: string[],
retrieve_state?: boolean,
homeassistant?: KeyValue,
}
type EntitySettings = {
type: 'device' | 'group'
ID: number | string,
friendlyName: string,
}
interface ResolvedEntity {
type: 'device' | 'group',
device: ZHDevice,
name: string,
}
interface ToZigbeeConverterGetMeta {message: {}, mapped: Definition | Definition[]}
interface ToZigbeeConverterResult {state: KeyValue, membersState: {[s: string]: KeyValue}, readAfterWriteTime?: number}
interface ToZigbeeConverter {
key: string[],
convertGet?: (entity: ZHEndpoint | ZHGroup, key: string, meta: ToZigbeeConverterGetMeta) => Promise<void>
convertSet?: (entity: ZHEndpoint | ZHGroup, key: string, value: any, meta: {state: KeyValue}) => Promise<ToZigbeeConverterResult>
}
// interface Logger {
// error: (message: string) => void;
// warn: (message: string) => void;
// debug: (message: string) => void;
// info: (message: string) => void;
// }
interface FromZigbeeConverter {
cluster: string,
type: string[] | string,
convert: (model: Definition, msg: KeyValue, publish: (payload: KeyValue) => void, options: KeyValue,
meta: {state: KeyValue, logger: any, device: ZHDevice}) => KeyValue,
}
interface DefinitionExposeFeature {name: string, endpoint?: string, property: string, value_max?: number, value_min?: number, value_off?: string, value_on?: string, value_step?: number, values: string[], access: number}
interface DefinitionExpose {type: string, name?: string, features?: DefinitionExposeFeature[], endpoint?: string, values?: string[], value_off?: string, value_on?: string, access: number, property: string, unit?: string, value_min?: number, value_max?: number}
interface Definition {
model: string
endpoint?: (device: ZHDevice) => {[s: string]: number}
toZigbee: ToZigbeeConverter[]
fromZigbee: FromZigbeeConverter[]
icon?: string
description: string
vendor: string
exposes: DefinitionExpose[] // TODO
configure?: (device: ZHDevice, coordinatorEndpoint: ZZHEndpoint, logger: unknown) => Promise<void>;
onEvent?: (type: string, data: KeyValue, device: ZHDevice, settings: KeyValue) => Promise<void>;
ota?: {
isUpdateAvailable: (device: ZHDevice, logger: unknown, data?: KeyValue) => Promise<boolean>;
updateToLatest: (device: ZHDevice, logger: unknown, onProgress: (progress: number, remaining: number) => void) => Promise<void>;
}
}
interface ResolvedDevice {
type: 'device',
definition?: Definition,
name: string,
endpoint: ZHEndpoint,
device: ZHDevice,
settings: {
friendlyName: string,
availability?: {timeout?: number} | boolean,
}
}
interface TempMQTT {
publish: (topic: string, payload: string, options: {}, base?: string, skipLog?: boolean, skipReceive?: boolean) => Promise<void>;
}
interface TempState {
get: (ID: string | number) => KeyValue | null;
remove: (ID: string | number) => void;
removeKey: (ID: string, keys: string[]) => void;
exists: (ID: string) => boolean;
}
interface ExternalConverterClass {
new(zigbee: Zigbee, mqtt: MQTT, state: TempState, publishEntityState: PublishEntityState,
eventBus: EventBus, settings: unknown, logger: unknown): ExternalConverterClass;
}
interface MQTTResponse {data: KeyValue, status: string, error?: string, transaction?: string}
type PublishEntityState = (ID: string | number, payload: KeyValue, stateChangeReason?: 'publishDebounce') => void;
}
+9
View File
@@ -0,0 +1,9 @@
declare module 'zigbee-herdsman-converters' {
export function findByDevice(device: zh.Device): zhc.Definition;
export function getConfigureKey(definition: zhc.Definition): string | number;
export const toZigbeeConverters: {[s: string]: zhc.ToZigbeeConverter};
export const definitions: zhc.Definition[];
export function addDeviceDefinition(definition: zhc.Definition): Promise<void>;
export function onEvent(type: string, data: KeyValue, device: zh.Device, settings: KeyValue,
state: KeyValue): Promise<void>;
}
+9
View File
@@ -0,0 +1,9 @@
declare module 'zigbee2mqtt-frontend' {
export function getPath(): string;
}
declare module 'connect-gzip-static' {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type RequestHandler = (req: any, res: any) => void;
export default function gzipStatic(root: string, options?: Record<string, unknown>): RequestHandler;
}
+6 -4
View File
@@ -13,15 +13,17 @@ function load(): void {
load();
export function joinPath(file: string): string {
return path.join(dataPath, file);
function joinPath(file: string): string {
return path.resolve(dataPath, file);
}
export function getPath(): string {
function getPath(): string {
return dataPath;
}
// eslint-disable-next-line camelcase
export function __testingOnly_reload(): void {
function testingOnlyReload(): void {
load();
}
export default {joinPath, getPath, testingOnlyReload};
-161
View File
@@ -1,161 +0,0 @@
const winston = require('winston');
const moment = require('moment');
const settings = require('./settings');
const path = require('path');
const fs = require('fs');
const fx = require('mkdir-recursive');
const rimraf = require('rimraf');
const colorizer = winston.format.colorize();
const assert = require('assert');
// What transports to enable
const output = settings.get().advanced.log_output;
// Directory to log to
const timestamp = moment(Date.now()).format('YYYY-MM-DD.HH-mm-ss');
const directory = settings.get().advanced.log_directory.replace('%TIMESTAMP%', timestamp);
const logFilename = settings.get().advanced.log_file.replace('%TIMESTAMP%', timestamp);
// Make sure that log directoy exsists 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;
if (fs.existsSync(current)) {
fs.unlinkSync(current);
}
fs.symlinkSync(actual, current);
}
}
const z2mToWinstonLevel = (level) => level === 'warn' ? 'warning' : level;
const winstonToZ2mLevel = (level) => level === 'warning' ? 'warn' : level;
// Determine the log level.
let level = settings.get().advanced.log_level;
const validLevels = ['info', 'error', 'warn', 'debug'];
assert(validLevels.includes(level), `'${level}' is not a valid log_level, use one of '${validLevels.join(', ')}'`);
level = z2mToWinstonLevel(level);
const levelWithCompensatedLength = {
'info': 'info ',
'error': 'error',
'warn': 'warn ',
'debug': 'debug',
};
/* istanbul ignore next */
const timestampFormat = () => moment().format(settings.get().advanced.timestamp_format);
// Setup default console logger
const transportsToUse = [
new winston.transports.Console({
level,
silent: !output.includes('console'),
format: winston.format.combine(
winston.format.timestamp({format: timestampFormat}),
winston.format.printf(/* istanbul ignore next */(info) => {
let {timestamp, level, message} = info;
level = winstonToZ2mLevel(level);
const prefix = colorizer.colorize(level, `Zigbee2MQTT:${levelWithCompensatedLength[level]}`);
return `${prefix} ${timestamp.split('.')[0]}: ${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 = {
filename: path.join(directory, logFilename),
json: false,
level,
format: winston.format.combine(
winston.format.timestamp({format: timestampFormat}),
winston.format.printf(/* istanbul ignore next */(info) => {
let {timestamp, level, message} = info;
level = winstonToZ2mLevel(level);
return `${levelWithCompensatedLength[level]} ${timestamp.split('.')[0]}: ${message}`;
}),
),
};
if (settings.get().advanced.log_rotation) {
transportFileOptions.tailable = true;
transportFileOptions.maxFiles = 3; // Keep last 3 files
transportFileOptions.maxsize = 10000000; // 10MB
}
if (output.includes('file')) {
transportsToUse.push(new winston.transports.File(transportFileOptions));
}
/* istanbul ignore next */
if (output.includes('syslog')) {
require('winston-syslog').Syslog;
const options = {
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();
transportsToUse.push(new winston.transports.Syslog(options));
}
// Create logger
const logger = winston.createLogger({transports: transportsToUse, levels: winston.config.syslog.levels});
// Cleanup any old log directory.
function cleanup() {
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, b) => b.birth - a.birth);
directories = directories.slice(10, directories.length);
directories.forEach((dir) => {
logger.debug(`Removing old log directory '${dir.path}'`);
rimraf.sync(dir.path);
});
}
}
// Print to user what logging is enabled
function logOutput() {
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}`);
}
logger.cleanup();
} else if (output.includes('console')) {
logger.info(`Logging to console only'`);
}
}
logger.addTransport = (transport) => {
transport.level = transportsToUse[0].level;
logger.add(transport);
};
logger.cleanup = cleanup;
logger.logOutput = logOutput;
logger.getLevel = () => winstonToZ2mLevel(transportsToUse[0].level);
logger.setLevel = (level) => {
level = z2mToWinstonLevel(level);
logger.transports.forEach((transport) => transport.level = level);
};
// winston.config.syslog.levels doesnt have warn, but is required for syslog.
/* istanbul ignore next */
logger.warn = (message) => logger.warning(message);
module.exports = logger;
+225
View File
@@ -0,0 +1,225 @@
import winston from 'winston';
import moment from 'moment';
import * as settings from './settings';
import path from 'path';
import fs from 'fs';
import fx from 'mkdir-recursive';
import {rimrafSync} from 'rimraf';
import assert from 'assert';
const colorizer = winston.format.colorize();
type Z2MLogLevel = 'warn' | 'debug' | 'info' | 'error';
type WinstonLogLevel = 'warning' | 'debug' | 'info' | 'error';
const z2mToWinstonLevel = (level: Z2MLogLevel): WinstonLogLevel => level === 'warn' ? 'warning' : level;
const winstonToZ2mLevel = (level: WinstonLogLevel): Z2MLogLevel => level === 'warning' ? 'warn' : level;
const levelWithCompensatedLength: {[s: string]: string} = {
'info': 'info ',
'error': 'error',
'warn': 'warn ',
'debug': 'debug',
};
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'),
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);
const plainPrefix = `Zigbee2MQTT:${levelWithCompensatedLength[l]}`;
let prefix = plainPrefix;
if (process.stdout.isTTY) {
prefix = colorizer.colorize(l, plainPrefix);
}
return `${prefix} ${timestamp.split('.')[0]}: ${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 (settings.get().advanced.log_rotation) {
transportFileOptions.tailable = true;
transportFileOptions.maxFiles = 3; // Keep last 3 files
transportFileOptions.maxsize = 10000000; // 10MB
}
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));
}
}
});
}
export default {
init, logOutput, warn, warning, error, info, debug, setLevel, getLevel, cleanup, addTransport, end,
winston: (): winston.Logger => logger,
};
File diff suppressed because it is too large Load Diff
+213 -195
View File
@@ -1,39 +1,53 @@
import * as data from './data';
import * as utils from './utils';
import data from './data';
import utils from './utils';
import objectAssignDeep from 'object-assign-deep';
import path from 'path';
import * as yaml from './yaml';
import Ajv from 'ajv';
import yaml from './yaml';
import Ajv, {ValidateFunction} from 'ajv';
import schemaJson from './settings.schema.json';
export const schema = schemaJson;
export let schema = schemaJson;
// @ts-ignore
schema = {};
objectAssignDeep(schema, schemaJson);
// TODO: check all
// Remove legacy settings from schema
{
delete schema.properties.advanced.properties.homeassistant_discovery_topic;
delete schema.properties.advanced.properties.homeassistant_legacy_entity_attributes;
delete schema.properties.advanced.properties.homeassistant_legacy_triggers;
delete schema.properties.advanced.properties.homeassistant_status_topic;
delete schema.properties.advanced.properties.soft_reset_timeout;
delete schema.properties.advanced.properties.report;
delete schema.properties.advanced.properties.baudrate;
delete schema.properties.advanced.properties.rtscts;
delete schema.properties.advanced.properties.ikea_ota_use_test_url;
delete schema.properties.experimental;
delete schemaJson.properties.whitelist;
delete schemaJson.properties.ban;
}
// 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(schema);
const ajvSetting = new Ajv({allErrors: true}).addKeyword('requiresRestart').compile(schemaJson);
const ajvRestartRequired = new Ajv({allErrors: true})
.addKeyword({keyword: 'requiresRestart', validate: (schema: unknown) => !schema}).compile(schema);
.addKeyword({keyword: 'requiresRestart', validate: (s: unknown) => !s}).compile(schemaJson);
const ajvRestartRequiredDeviceOptions = new Ajv({allErrors: true})
.addKeyword({keyword: 'requiresRestart', validate: (s: unknown) => !s}).compile(schemaJson.definitions.device);
const ajvRestartRequiredGroupOptions = new Ajv({allErrors: true})
.addKeyword({keyword: 'requiresRestart', validate: (s: unknown) => !s}).compile(schemaJson.definitions.group);
const defaults: RecursivePartial<Settings> = {
passlist: [],
blocklist: [],
// Deprecated: use block/passlist
whitelist: [],
ban: [],
permit_join: false,
external_converters: [],
mqtt: {
base_topic: 'zigbee2mqtt',
include_device_information: false,
/**
* Configurable force disable retain flag on mqtt publish.
* https://github.com/Koenkk/zigbee2mqtt/pull/4948
*/
force_disable_retain: false,
},
serial: {
disable_led: false,
},
device_options: {},
passlist: [],
blocklist: [],
map_options: {
graphviz: {
colors: {
@@ -54,12 +68,14 @@ const defaults: RecursivePartial<Settings> = {
},
},
},
experimental: {
// json or attribute or attribute_and_json
output: 'json',
ota: {
update_check_interval: 24 * 60,
disable_automatic_update_check: false,
},
device_options: {},
advanced: {
legacy_api: true,
legacy_availability_payload: true,
log_rotation: true,
log_symlink_current: false,
log_output: ['console', 'file'],
@@ -67,126 +83,146 @@ const defaults: RecursivePartial<Settings> = {
log_file: 'log.txt',
log_level: /* istanbul ignore next */ process.env.DEBUG ? 'debug' : 'info',
log_syslog: {},
soft_reset_timeout: 0,
pan_id: 0x1a62,
ext_pan_id: [0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD],
channel: 11,
adapter_concurrent: null,
adapter_delay: null,
// Availability timeout in seconds, disabled by default.
availability_timeout: 0,
availability_blocklist: [],
availability_passlist: [],
// Deprecated, use block/passlist
availability_blacklist: [],
availability_whitelist: [],
/**
* Home Assistant requires ALL attributes to be present in ALL MQTT messages send by the device.
* https://community.home-assistant.io/t/missing-value-with-mqtt-only-last-data-set-is-shown/47070/9
*
* Therefore Zigbee2MQTT BY DEFAULT caches all values and resend it with every message.
* advanced.cache_state in configuration.yaml allows to configure this.
* https://www.zigbee2mqtt.io/configuration/configuration.html
*/
cache_state: true,
cache_state_persistent: true,
cache_state_send_on_startup: true,
/**
* Add a last_seen attribute to mqtt messages, contains date/time of zigbee message arrival
* "ISO_8601": ISO 8601 format
* "ISO_8601_local": Local ISO 8601 format (instead of UTC-based)
* "epoch": milliseconds elapsed since the UNIX epoch
* "disable": no last_seen attribute (default)
*/
last_seen: 'disable',
// Optional: Add an elapsed attribute to MQTT messages, contains milliseconds since the previous msg
elapsed: false,
/**
* https://github.com/Koenkk/zigbee2mqtt/issues/685#issuecomment-449112250
*
* Network key will serve as the encryption key of your network.
* Changing this will require you to repair your devices.
*/
network_key: [1, 3, 5, 7, 9, 11, 13, 15, 0, 2, 4, 6, 8, 10, 12, 13],
/**
* Enables reporting feature
*/
report: false,
/**
* Home Assistant discovery topic
*/
homeassistant_discovery_topic: 'homeassistant',
/**
* Home Assistant status topic
*/
homeassistant_status_topic: 'hass/status',
/**
* Home Assistant legacy entity attributes, when enabled:
* Zigbee2MQTT will send additional states as attributes with each entity.
* For example, A temperature & humidity sensor will have 2 entities for
* the temperature and humidity, with this setting enabled both entities
* will also have an temperature and humidity attribute.
*/
homeassistant_legacy_entity_attributes: true,
/**
* Home Assistant legacy triggers, when enabled:
* - Zigbee2mqt will send an empty 'action' or 'click' after one has been send
* - A 'sensor_action' and 'sensor_click' will be discoverd
*/
homeassistant_legacy_triggers: true,
/**
* Configurable timestampFormat
* https://github.com/Koenkk/zigbee2mqtt/commit/44db557a0c83f419d66755d14e460cd78bd6204e
*/
timestamp_format: 'YYYY-MM-DD HH:mm:ss',
output: 'json',
// Everything below is deprecated
availability_blocklist: [],
availability_passlist: [],
availability_blacklist: [],
availability_whitelist: [],
soft_reset_timeout: 0,
report: false,
},
ota: {
/**
* Minimal time delta in minutes between polling third party server for potential firmware updates
*/
update_check_interval: 24 * 60,
/**
* Completely disallow Zigbee devices to initiate a search for a potential firmware update.
* If set to true, only a user-initiated update search will be possible.
*/
disable_automatic_update_check: false,
},
external_converters: [],
};
let _settings: Partial<Settings>;
let _settingsWithDefaults: Settings;
function loadSettingsWithDefaults(): void {
_settingsWithDefaults = objectAssignDeep({}, defaults, getInternalSettings()) as Settings;
if (!_settingsWithDefaults.devices) {
_settingsWithDefaults.devices = {};
}
if (!_settingsWithDefaults.groups) {
_settingsWithDefaults.groups = {};
}
if (_settingsWithDefaults.homeassistant) {
const defaults = {discovery_topic: 'homeassistant', status_topic: 'hass/status',
legacy_entity_attributes: true, legacy_triggers: true};
const sLegacy = {};
if (_settingsWithDefaults.advanced) {
for (const key of ['homeassistant_legacy_triggers', 'homeassistant_discovery_topic',
'homeassistant_legacy_entity_attributes', 'homeassistant_status_topic']) {
// @ts-ignore
if (_settingsWithDefaults.advanced[key] !== undefined) {
// @ts-ignore
sLegacy[key.replace('homeassistant_', '')] = _settingsWithDefaults.advanced[key];
}
}
}
const s = typeof _settingsWithDefaults.homeassistant === 'object' ? _settingsWithDefaults.homeassistant : {};
// @ts-ignore
_settingsWithDefaults.homeassistant = {};
objectAssignDeep(_settingsWithDefaults.homeassistant, defaults, sLegacy, s);
}
if (_settingsWithDefaults.availability || _settingsWithDefaults.advanced?.availability_timeout) {
const defaults = {};
const s = typeof _settingsWithDefaults.availability === 'object' ? _settingsWithDefaults.availability : {};
// @ts-ignore
_settingsWithDefaults.availability = {};
objectAssignDeep(_settingsWithDefaults.availability, defaults, s);
}
if (_settingsWithDefaults.frontend) {
const defaults = {port: 8080, auth_token: false, host: '0.0.0.0'};
const s = typeof _settingsWithDefaults.frontend === 'object' ? _settingsWithDefaults.frontend : {};
// @ts-ignore
_settingsWithDefaults.frontend = {};
objectAssignDeep(_settingsWithDefaults.frontend, defaults, s);
}
if (_settings.advanced?.hasOwnProperty('baudrate') && _settings.serial?.baudrate == null) {
// @ts-ignore
_settingsWithDefaults.serial.baudrate = _settings.advanced.baudrate;
}
if (_settings.advanced?.hasOwnProperty('rtscts') && _settings.serial?.rtscts == null) {
// @ts-ignore
_settingsWithDefaults.serial.rtscts = _settings.advanced.rtscts;
}
if (_settings.advanced?.hasOwnProperty('ikea_ota_use_test_url') && _settings.ota?.ikea_ota_use_test_url == null) {
// @ts-ignore
_settingsWithDefaults.ota.ikea_ota_use_test_url = _settings.advanced.ikea_ota_use_test_url;
}
// @ts-ignore
if (_settings.experimental?.hasOwnProperty('transmit_power') && _settings.advanced?.transmit_power == null) {
// @ts-ignore
_settingsWithDefaults.advanced.transmit_power = _settings.experimental.transmit_power;
}
// @ts-ignore
if (_settings.experimental?.hasOwnProperty('output') && _settings.advanced?.output == null) {
// @ts-ignore
_settingsWithDefaults.advanced.output = _settings.experimental.output;
}
// @ts-ignore
_settingsWithDefaults.ban && _settingsWithDefaults.blocklist.push(..._settingsWithDefaults.ban);
// @ts-ignore
_settingsWithDefaults.whitelist && _settingsWithDefaults.passlist.push(..._settingsWithDefaults.whitelist);
}
function parseValueRef(text: string): {filename: string, key: string} | null {
const match = /!(.*) (.*)/g.exec(text);
if (match) {
let filename = match[1];
// This is mainly for backward compatibility.
if (!filename.endsWith('.yaml') && !filename.endsWith('.yml')) {
filename += '.yaml';
}
return {filename, key: match[2]};
} else {
return null;
}
}
function write(): void {
const settings = getInternalSettings();
/* eslint-disable-line */ // @ts-ignore
const toWrite = objectAssignDeep.noMutate(settings);
const toWrite: KeyValue = objectAssignDeep({}, settings);
// Read settings to check if we have to split devices/groups into separate file.
const actual = yaml.read(file);
// In case the setting is defined in a separte file (e.g. !secret network_key) update it there.
// In case the setting is defined in a separate file (e.g. !secret network_key) update it there.
for (const path of [
['mqtt', 'server'],
['mqtt', 'user'],
['mqtt', 'password'],
['advanced', 'network_key'],
['frontend', 'auth_token'],
]) {
if (actual[path[0]] && actual[path[0]][path[1]]) {
const match = /!(.*) (.*)/g.exec(actual[path[0]][path[1]]);
if (match) {
yaml.updateIfChanged(data.joinPath(`${match[1]}.yaml`), match[2], toWrite[path[0]][path[1]]);
const ref = parseValueRef(actual[path[0]][path[1]]);
if (ref) {
yaml.updateIfChanged(data.joinPath(ref.filename), ref.key, toWrite[path[0]][path[1]]);
toWrite[path[0]][path[1]] = actual[path[0]][path[1]];
}
}
@@ -194,10 +230,9 @@ function write(): void {
// Write devices/groups to separate file if required.
const writeDevicesOrGroups = (type: 'devices' | 'groups'): void => {
if (typeof actual[type] === 'string' || Array.isArray(actual[type])) {
if (typeof actual[type] === 'string' || (Array.isArray(actual[type]) && actual[type].length > 0)) {
const fileToWrite = Array.isArray(actual[type]) ? actual[type][0] : actual[type];
/* eslint-disable-line */ // @ts-ignore
const content = objectAssignDeep.noMutate(settings[type]);
const content = objectAssignDeep({}, settings[type]);
// If an array, only write to first file and only devices which are not in the other files.
if (Array.isArray(actual[type])) {
@@ -218,8 +253,7 @@ function write(): void {
yaml.writeIfChanged(file, toWrite);
_settings = read();
/* eslint-disable-line */ // @ts-ignore
_settingsWithDefaults = objectAssignDeep.noMutate(defaults, getInternalSettings());
loadSettingsWithDefaults();
}
export function validate(): string[] {
@@ -253,15 +287,18 @@ export function validate(): string[] {
// Verify that all friendly names are unique
const names: string[] = [];
const check = (name: string): void => {
if (names.includes(name)) errors.push(`Duplicate friendly_name '${name}' found`);
errors.push(...utils.validateFriendlyName(name));
names.push(name);
const check = (e: DeviceOptions | GroupOptions): void => {
if (names.includes(e.friendly_name)) errors.push(`Duplicate friendly_name '${e.friendly_name}' found`);
errors.push(...utils.validateFriendlyName(e.friendly_name));
names.push(e.friendly_name);
if (e.qos != null && ![0, 1, 2].includes(e.qos)) {
errors.push(`QOS for '${e.friendly_name}' not valid, should be 0, 1 or 2 got ${e.qos}`);
}
};
const settingsWithDefaults = get();
Object.values(settingsWithDefaults.devices).forEach((d) => check(d.friendly_name));
Object.values(settingsWithDefaults.groups).forEach((g) => check(g.friendly_name));
Object.values(settingsWithDefaults.devices).forEach((d) => check(d));
Object.values(settingsWithDefaults.groups).forEach((g) => check(g));
if (settingsWithDefaults.mqtt.version !== 5) {
for (const device of Object.values(settingsWithDefaults.devices)) {
@@ -273,7 +310,7 @@ export function validate(): string[] {
const checkAvailabilityList = (list: string[], type: string): void => {
list.forEach((e) => {
if (!getEntity(e)) {
if (!getDevice(e)) {
errors.push(`Non-existing entity '${e}' specified in '${type}'`);
}
});
@@ -289,37 +326,42 @@ export function validate(): string[] {
function read(): Settings {
const s = yaml.read(file) as Settings;
applyEnvironmentVariables(s);
// Read !secret MQTT username and password if set
// eslint-disable-next-line
const interpetValue = (value: any): any => {
const re = /!(.*) (.*)/g;
const match = re.exec(value);
if (match) {
const file = data.joinPath(`${match[1]}.yaml`);
const key = match[2];
return yaml.read(file)[key];
const interpretValue = (value: any): any => {
const ref = parseValueRef(value);
if (ref) {
return yaml.read(data.joinPath(ref.filename))[ref.key];
} else {
return value;
}
};
if (s.mqtt?.user && s.mqtt?.password) {
s.mqtt.user = interpetValue(s.mqtt.user);
s.mqtt.password = interpetValue(s.mqtt.password);
if (s.mqtt?.user) {
s.mqtt.user = interpretValue(s.mqtt.user);
}
if (s.mqtt?.password) {
s.mqtt.password = interpretValue(s.mqtt.password);
}
if (s.mqtt?.server) {
s.mqtt.server = interpretValue(s.mqtt.server);
}
if (s.advanced?.network_key) {
s.advanced.network_key = interpetValue(s.advanced.network_key);
s.advanced.network_key = interpretValue(s.advanced.network_key);
}
if (s.frontend?.auth_token) {
s.frontend.auth_token = interpetValue(s.frontend.auth_token);
s.frontend.auth_token = interpretValue(s.frontend.auth_token);
}
// Read devices/groups configuration from separate file if specified.
const readDevicesOrGroups = (type: 'devices' | 'groups'): void => {
if (typeof s[type] === 'string' || Array.isArray(s[type])) {
if (typeof s[type] === 'string' || (Array.isArray(s[type]) && Array(s[type]).length > 0)) {
/* eslint-disable-line */ // @ts-ignore
const files: string[] = Array.isArray(s[type]) ? s[type] : [s[type]];
s[type] = {};
@@ -354,7 +396,11 @@ function applyEnvironmentVariables(settings: Partial<Settings>): void {
}, settings);
if (type.indexOf('object') >= 0 || type.indexOf('array') >= 0) {
setting[key] = JSON.parse(process.env[envVariableName]);
try {
setting[key] = JSON.parse(process.env[envVariableName]);
} catch (error) {
setting[key] = process.env[envVariableName];
}
} else if (type.indexOf('number') >= 0) {
/* eslint-disable-line */ // @ts-ignore
setting[key] = process.env[envVariableName] * 1;
@@ -371,7 +417,7 @@ function applyEnvironmentVariables(settings: Partial<Settings>): void {
if (typeof obj[key] === 'object' && obj[key]) {
const newPath = [...path];
if (key !== 'properties') {
if (key !== 'properties' && key !== 'oneOf' && !Number.isInteger(Number(key))) {
newPath.push(key);
}
iterate(obj[key], newPath);
@@ -379,13 +425,12 @@ function applyEnvironmentVariables(settings: Partial<Settings>): void {
}
});
};
iterate(schema.properties, []);
iterate(schemaJson.properties, []);
}
function getInternalSettings(): Partial<Settings> {
if (!_settings) {
_settings = read();
applyEnvironmentVariables(_settings);
}
return _settings;
@@ -393,16 +438,7 @@ function getInternalSettings(): Partial<Settings> {
export function get(): Settings {
if (!_settingsWithDefaults) {
/* eslint-disable-line */ // @ts-ignore
_settingsWithDefaults = objectAssignDeep.noMutate(defaults, getInternalSettings());
}
if (!_settingsWithDefaults.devices) {
_settingsWithDefaults.devices = {};
}
if (!_settingsWithDefaults.groups) {
_settingsWithDefaults.groups = {};
loadSettingsWithDefaults();
}
return _settingsWithDefaults;
@@ -436,7 +472,7 @@ export function apply(newSettings: Record<string, unknown>): boolean {
throw new Error(`${error.instancePath.substring(1)} ${error.message}`);
}
getInternalSettings(); // Ensure _settings is intialized.
getInternalSettings(); // Ensure _settings is initialized.
/* eslint-disable-line */ // @ts-ignore
_settings = objectAssignDeep.noMutate(_settings, newSettings);
write();
@@ -447,30 +483,30 @@ export function apply(newSettings: Record<string, unknown>): boolean {
return restartRequired;
}
export function getGroup(IDorName: string | number): GroupSettings {
export function getGroup(IDorName: string | number): GroupOptions {
const settings = get();
const byID = settings.groups[IDorName];
if (byID) {
return {devices: [], ...byID, ID: Number(IDorName), friendlyName: byID.friendly_name};
return {devices: [], ...byID, ID: Number(IDorName)};
}
for (const [ID, group] of Object.entries(settings.groups)) {
if (group.friendly_name === IDorName) {
return {devices: [], ...group, ID: Number(ID), friendlyName: group.friendly_name};
return {devices: [], ...group, ID: Number(ID)};
}
}
return null;
}
export function getGroups(): GroupSettings[] {
export function getGroups(): GroupOptions[] {
const settings = get();
return Object.entries(settings.groups).map(([ID, group]) => {
return {devices: [], ...group, ID: Number(ID), friendlyName: group.friendly_name};
return {devices: [], ...group, ID: Number(ID)};
});
}
function getGroupThrowIfNotExists(IDorName: string): GroupSettings {
function getGroupThrowIfNotExists(IDorName: string): GroupOptions {
const group = getGroup(IDorName);
if (!group) {
throw new Error(`Group '${IDorName}' does not exist`);
@@ -479,23 +515,23 @@ function getGroupThrowIfNotExists(IDorName: string): GroupSettings {
return group;
}
export function getDevice(IDorName: string): DeviceSettings {
export function getDevice(IDorName: string): DeviceOptions {
const settings = get();
const byID = settings.devices[IDorName];
if (byID) {
return {...byID, ID: IDorName, friendlyName: byID.friendly_name};
return {...byID, ID: IDorName};
}
for (const [ID, device] of Object.entries(settings.devices)) {
if (device.friendly_name === IDorName) {
return {...device, ID, friendlyName: device.friendly_name};
return {...device, ID};
}
}
return null;
}
function getDeviceThrowIfNotExists(IDorName: string): DeviceSettings {
function getDeviceThrowIfNotExists(IDorName: string): DeviceOptions {
const device = getDevice(IDorName);
if (!device) {
throw new Error(`Device '${IDorName}' does not exist`);
@@ -504,21 +540,7 @@ function getDeviceThrowIfNotExists(IDorName: string): DeviceSettings {
return device;
}
export function getEntity(IDorName: string): EntitySettings {
const device = getDevice(IDorName);
if (device) {
return {...device, type: 'device'};
}
const group = getGroup(IDorName);
if (group) {
return {...group, type: 'group'};
}
return null;
}
export function addDevice(ID: string): DeviceSettings {
export function addDevice(ID: string): DeviceOptions {
if (getDevice(ID)) {
throw new Error(`Device '${ID}' already exists`);
}
@@ -534,17 +556,17 @@ export function addDevice(ID: string): DeviceSettings {
return getDevice(ID);
}
export function whitelistDevice(ID: string): void {
export function addDeviceToPasslist(ID: string): void {
const settings = getInternalSettings();
if (!settings.whitelist) {
settings.whitelist = [];
if (!settings.passlist) {
settings.passlist = [];
}
if (settings.whitelist.includes(ID)) {
throw new Error(`Device '${ID}' already whitelisted`);
if (settings.passlist.includes(ID)) {
throw new Error(`Device '${ID}' already in passlist`);
}
settings.whitelist.push(ID);
settings.passlist.push(ID);
write();
}
@@ -558,16 +580,6 @@ export function blockDevice(ID: string): void {
write();
}
export function banDevice(ID: string): void {
const settings = getInternalSettings();
if (!settings.ban) {
settings.ban = [];
}
settings.ban.push(ID);
write();
}
export function removeDevice(IDorName: string): void {
const device = getDeviceThrowIfNotExists(IDorName);
const settings = getInternalSettings();
@@ -576,7 +588,7 @@ export function removeDevice(IDorName: string): void {
// Remove device from groups
if (settings.groups) {
const regex =
new RegExp(`^(${device.friendlyName}|${device.ID})(/(\\d|${utils.endpointNames.join('|')}))?$`);
new RegExp(`^(${device.friendly_name}|${device.ID})(/(\\d|${utils.endpointNames.join('|')}))?$`);
for (const group of Object.values(settings.groups).filter((g) => g.devices)) {
group.devices = group.devices.filter((device) => !device.match(regex));
}
@@ -585,7 +597,7 @@ export function removeDevice(IDorName: string): void {
write();
}
export function addGroup(name: string, ID?: string): GroupSettings {
export function addGroup(name: string, ID?: string): GroupOptions {
utils.validateFriendlyName(name, true);
if (getGroup(name) || getDevice(name)) {
throw new Error(`friendly_name '${name}' is already in use`);
@@ -658,21 +670,27 @@ export function removeGroup(IDorName: string | number): void {
write();
}
export function changeEntityOptions(IDorName: string, newOptions: KeyValue): void {
export function changeEntityOptions(IDorName: string, newOptions: KeyValue): boolean {
const settings = getInternalSettings();
delete newOptions.friendly_name;
delete newOptions.devices;
let validator: ValidateFunction;
if (getDevice(IDorName)) {
objectAssignDeep(settings.devices[getDevice(IDorName).ID], newOptions);
utils.removeNullPropertiesFromObject(settings.devices[getDevice(IDorName).ID]);
validator = ajvRestartRequiredDeviceOptions;
} else if (getGroup(IDorName)) {
objectAssignDeep(settings.groups[getGroup(IDorName).ID], newOptions);
utils.removeNullPropertiesFromObject(settings.groups[getGroup(IDorName).ID]);
validator = ajvRestartRequiredGroupOptions;
} else {
throw new Error(`Device or group '${IDorName}' does not exist`);
}
write();
validator(newOptions);
const restartRequired = validator.errors && !!validator.errors.find((e) => e.keyword === 'requiresRestart');
return restartRequired;
}
export function changeFriendlyName(IDorName: string, newName: string): void {
+172 -90
View File
@@ -1,12 +1,11 @@
import equals from 'fast-deep-equal/es6';
import humanizeDuration from 'humanize-duration';
import * as data from './data';
import data from './data';
import vm from 'vm';
import fs from 'fs';
import path from 'path';
import {Endpoint} from 'zigbee-herdsman/dist/controller/model';
// TODO: check all
import {detailedDiff} from 'deep-object-diff';
import objectAssignDeep from 'object-assign-deep';
// construct a local ISO8601 string (instead of UTC-based)
// Example:
@@ -30,7 +29,7 @@ function toLocalISOString(date: Date): string {
':' + pad(tzOffset % 60);
}
export const endpointNames = [
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',
@@ -41,22 +40,21 @@ export const endpointNames = [
'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',
'heat', 'cool', 'water', 'meter', 'wifi', 'no_occupancy_since',
];
export function capitalize(s: string): string {
function capitalize(s: string): string {
return s[0].toUpperCase() + s.slice(1);
}
export async function getZigbee2MQTTVersionSimple(): Promise<string> {
const packageJSON = await import('../..' + '/package.json');
return packageJSON.version;
}
export async function getZigbee2MQTTVersion(): Promise<{commitHash: string, version: string}> {
async function getZigbee2MQTTVersion(includeCommitHash=true): Promise<{commitHash: string, version: string}> {
const git = await import('git-last-commit');
const packageJSON = await import('../..' + '/package.json');
if (!includeCommitHash) {
return {version: packageJSON.version, commitHash: null};
}
return new Promise((resolve) => {
const version = packageJSON.version;
@@ -74,18 +72,19 @@ export async function getZigbee2MQTTVersion(): Promise<{commitHash: string, vers
commitHash = commit.shortHash;
}
commitHash = commitHash.trim();
resolve({commitHash, version});
});
});
}
export async function getDependencyVersion(depend: string): Promise<{version: string}> {
const packageJSON = await import(path.join(__dirname, '..', '..', 'node_modules', depend, 'package.json'));
async function getDependencyVersion(depend: string): Promise<{version: string}> {
const packageJSON = await import(path.join(require.resolve(depend), '..', '..', 'package.json'));
const version = packageJSON.version;
return {version};
}
export function formatDate(time: number, type: 'ISO_8601' | 'ISO_8601_local' | 'epoch' | 'relative'): string | number {
function formatDate(time: number, type: 'ISO_8601' | 'ISO_8601_local' | 'epoch' | 'relative'): string | number {
if (type === 'ISO_8601') return new Date(time).toISOString();
else if (type === 'ISO_8601_local') return toLocalISOString(new Date(time));
else if (type === 'epoch') return time;
@@ -94,7 +93,7 @@ export function formatDate(time: number, type: 'ISO_8601' | 'ISO_8601_local' | '
}
}
export function objectHasProperties(object: {[s: string]: unknown}, properties: string[]): boolean {
function objectHasProperties(object: {[s: string]: unknown}, properties: string[]): boolean {
for (const property of properties) {
if (!object.hasOwnProperty(property)) {
return false;
@@ -104,7 +103,7 @@ export function objectHasProperties(object: {[s: string]: unknown}, properties:
return true;
}
export function equalsPartial(object: {[s: string]: unknown}, expected: {[s: string]: unknown}): boolean {
function equalsPartial(object: KeyValue, expected: KeyValue): boolean {
for (const [key, value] of Object.entries(expected)) {
if (!equals(object[key], value)) {
return false;
@@ -114,13 +113,12 @@ export function equalsPartial(object: {[s: string]: unknown}, expected: {[s: str
return true;
}
export function getObjectProperty(object: {[s: string]: unknown}, key: string, defaultValue: unknown): unknown {
function getObjectProperty(object: KeyValue, key: string, defaultValue: unknown): unknown {
return object && object.hasOwnProperty(key) ? object[key] : defaultValue;
}
export function getResponse(request: KeyValue | string, data: KeyValue, error: string): MQTTResponse {
const response: {data: unknown, status: string, error?: string, transaction?: string} =
{data, status: error ? 'error' : 'ok'};
function getResponse(request: KeyValue | string, data: KeyValue, error: string): MQTTResponse {
const response: MQTTResponse = {data, status: error ? 'error' : 'ok'};
if (error) response.error = error;
if (typeof request === 'object' && request.hasOwnProperty('transaction')) {
response.transaction = request.transaction;
@@ -128,16 +126,16 @@ export function getResponse(request: KeyValue | string, data: KeyValue, error: s
return response;
}
export function parseJSON(value: string, failedReturnValue: string): KeyValue | string {
function parseJSON(value: string, fallback: string): KeyValue | string {
try {
return JSON.parse(value);
} catch (e) {
return failedReturnValue;
return fallback;
}
}
export function loadModuleFromText(moduleCode: string): unknown {
const moduleFakePath = path.join(__dirname, 'externally-loaded.js');
function loadModuleFromText(moduleCode: string, name?: string): unknown {
const moduleFakePath = path.join(__dirname, '..', '..', 'data', 'extension', name || 'externally-loaded.js');
const sandbox = {
require: require,
module: {},
@@ -154,14 +152,13 @@ export function loadModuleFromText(moduleCode: string): unknown {
return sandbox.module.exports;
}
export function loadModuleFromFile(modulePath: string): unknown {
function loadModuleFromFile(modulePath: string): unknown {
const moduleCode = fs.readFileSync(modulePath, {encoding: 'utf8'});
return loadModuleFromText(moduleCode);
}
/* eslint-disable-next-line */
export function* getExternalConvertersDefinitions(settings: any): any {
const externalConverters = settings.get().external_converters;
function* getExternalConvertersDefinitions(settings: Settings): Generator<zhc.ExternalDefinition> {
const externalConverters = settings.external_converters;
for (const moduleName of externalConverters) {
let converter;
@@ -182,7 +179,7 @@ export function* getExternalConvertersDefinitions(settings: any): any {
}
}
export function removeNullPropertiesFromObject(obj: KeyValue): void {
function removeNullPropertiesFromObject(obj: KeyValue): void {
for (const key of Object.keys(obj)) {
const value = obj[key];
if (value == null) {
@@ -193,24 +190,13 @@ export function removeNullPropertiesFromObject(obj: KeyValue): void {
}
}
export function getKey(
object: KeyValue, value: unknown, fallback: unknown, convertTo: (v: unknown) => unknown): unknown {
for (const key in object) {
if (object[key]===value) {
return convertTo ? convertTo(key) : key;
}
}
return fallback;
}
export function toNetworkAddressHex(value: number): string {
function toNetworkAddressHex(value: number): string {
const hex = value.toString(16);
return `0x${'0'.repeat(4 - hex.length)}${hex}`;
}
// eslint-disable-next-line
export function toSnakeCase(value: string | KeyValue): any {
function toSnakeCase(value: string | KeyValue): any {
if (typeof value === 'object') {
value = {...value};
for (const key of Object.keys(value)) {
@@ -226,7 +212,44 @@ export function toSnakeCase(value: string | KeyValue): any {
}
}
export function validateFriendlyName(name: string, throwFirstError=false): string[] {
function charRange(start: string, stop: string): number[] {
const result = [];
for (let idx=start.charCodeAt(0), end=stop.charCodeAt(0); idx <=end; ++idx) {
result.push(idx);
}
return result;
}
const controlCharacters = [
...charRange('\u0000', '\u001F'),
...charRange('\u007f', '\u009F'),
...charRange('\ufdd0', '\ufdef'),
];
function containsControlCharacter(str: string): boolean {
for (let i = 0; i < str.length; i++) {
const ch = str.charCodeAt(i);
if (controlCharacters.includes(ch) || [0xFFFE, 0xFFFF].includes(ch & 0xFFFF)) {
return true;
}
}
return false;
}
function getAllFiles(path_: string): string[] {
const result = [];
for (let item of fs.readdirSync(path_)) {
item = path.join(path_, item);
if (fs.lstatSync(item).isFile()) {
result.push(item);
} else {
result.push(...getAllFiles(item));
}
}
return result;
}
function validateFriendlyName(name: string, throwFirstError=false): string[] {
const errors = [];
for (const endpointName of endpointNames) {
if (name.toLowerCase().endsWith('/' + endpointName)) {
@@ -236,7 +259,7 @@ export function validateFriendlyName(name: string, throwFirstError=false): strin
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 (name.endsWith(String.fromCharCode(0))) errors.push(`friendly_name is not allowed to contain null char`);
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('+')) {
@@ -250,79 +273,138 @@ export function validateFriendlyName(name: string, throwFirstError=false): strin
return errors;
}
export function sleep(seconds: number): Promise<void> {
function sleep(seconds: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, seconds * 1000));
}
export function sanitizeImageParameter(parameter: string): string {
function sanitizeImageParameter(parameter: string): string {
const replaceByDash = [/\?/g, /&/g, /[^a-z\d\- _./:]/gi];
let sanitized = parameter;
replaceByDash.forEach((r) => sanitized = sanitized.replace(r, '-'));
return sanitized;
}
export function isAvailabilityEnabledForDevice(device: Device, settings: Settings): boolean {
/* istanbul ignore next */
if (!settings.experimental.availability_new) return false;
function isAvailabilityEnabledForEntity(entity: Device | Group, settings: Settings): boolean {
if (entity.isGroup()) {
return !entity.membersDevices().map((d) => isAvailabilityEnabledForEntity(d, settings)).includes(false);
}
if (device.settings.hasOwnProperty('availability')) {
return !!device.settings.availability;
if (entity.options.hasOwnProperty('availability')) {
return !!entity.options.availability;
}
// availability_timeout = deprecated
const enabledGlobal = settings.advanced.availability_timeout || settings.availability;
if (!enabledGlobal) return false;
const passlist = settings.advanced.availability_passlist.concat(settings.advanced.availability_whitelist);
if (passlist.length > 0) {
return passlist.includes(device.name) || passlist.includes(device.ieeeAddr);
}
const blocklist = settings.advanced.availability_blacklist.concat(settings.advanced.availability_blocklist);
return !blocklist.includes(device.name) && !blocklist.includes(device.ieeeAddr);
}
/* istanbul ignore next */
export function isAvailabilityEnabledForDeviceLegacy(rd: ResolvedDevice, settings: Settings): boolean {
if (!settings.experimental.availability_new) return false;
if (rd.settings.hasOwnProperty('availability')) {
return !!rd.settings.availability;
}
// availability_timeout = deprecated
const enabledGlobal = settings.advanced.availability_timeout || settings.availability;
if (!enabledGlobal) return false;
if (entity.isDevice() && entity.options.disabled) return false;
const passlist = settings.advanced.availability_passlist.concat(settings.advanced.availability_whitelist);
if (passlist.length > 0) {
return passlist.includes(rd.name) || passlist.includes(rd.device.ieeeAddr);
return passlist.includes(entity.name) || passlist.includes(entity.ieeeAddr);
}
const blocklist = settings.advanced.availability_blacklist.concat(settings.advanced.availability_blocklist);
return !blocklist.includes(rd.name) && !blocklist.includes(rd.device.ieeeAddr);
}
export function isXiaomiDevice(device: ZHDevice): boolean {
const xiaomiManufacturerID = [4151, 4447];
return device.modelID !== 'lumi.router' && xiaomiManufacturerID.includes(device.manufacturerID) &&
(!device.manufacturerName || !device.manufacturerName.startsWith('Trust'));
}
export function isIkeaTradfriDevice(device: ZHDevice): boolean {
return [4476].includes(device.manufacturerID);
return !blocklist.includes(entity.name) && !blocklist.includes(entity.ieeeAddr);
}
const entityIDRegex = new RegExp(`^(.+?)(?:/(${endpointNames.join('|')}|\\d+))?$`);
export function parseEntityID(ID: string): {ID: string, endpoint: string} {
function parseEntityID(ID: string): {ID: string, endpoint: string} {
const match = ID.match(entityIDRegex);
return match && {ID: match[1], endpoint: match[2]};
}
export function isEndpoint(obj: unknown): obj is Endpoint {
function isEndpoint(obj: unknown): obj is zh.Endpoint {
return obj.constructor.name.toLowerCase() === 'endpoint';
}
export const hours = (hours: number): number => 1000 * 60 * 60 * hours;
export const minutes = (minutes: number): number => 1000 * 60 * minutes;
export const seconds = (seconds: number): number => 1000 * seconds;
function flatten<Type>(arr: Type[][]): Type[] {
return [].concat(...arr);
}
function arrayUnique<Type>(arr: Type[]): Type[] {
return [...new Set(arr)];
}
function isZHGroup(obj: unknown): obj is zh.Group {
return obj.constructor.name.toLowerCase() === 'group';
}
function availabilityPayload(state: 'online' | 'offline', settings: Settings): string {
return settings.advanced.legacy_availability_payload ? state : JSON.stringify({state});
}
const hours = (hours: number): number => 1000 * 60 * 60 * hours;
const minutes = (minutes: number): number => 1000 * 60 * minutes;
const seconds = (seconds: number): number => 1000 * seconds;
function publishLastSeen(data: eventdata.LastSeenChanged, settings: Settings, allowMessageEmitted: boolean,
publishEntityState: PublishEntityState): void {
/**
* Prevent 2 MQTT publishes when 1 message event is received;
* - In case reason == messageEmitted, receive.ts will only call this when it did not publish a
* message based on the received zigbee message. In this case allowMessageEmitted has to be true.
* - In case reason !== messageEmitted, controller.ts will call this based on the zigbee-herdsman
* lastSeenChanged event.
*/
const allow = data.reason !== 'messageEmitted' || (data.reason === 'messageEmitted' && allowMessageEmitted);
if (settings.advanced.last_seen && settings.advanced.last_seen !== 'disable' && allow) {
publishEntityState(data.device, {}, 'lastSeenChanged');
}
}
function filterProperties(filter: string[], data: KeyValue): void {
if (filter) {
for (const property of Object.keys(data)) {
if (filter.find((p) => property.match(`^${p}$`))) {
delete data[property];
}
}
}
}
function clone(obj: KeyValue): KeyValue {
return JSON.parse(JSON.stringify(obj));
}
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;
}
export default {
endpointNames, capitalize, getZigbee2MQTTVersion, getDependencyVersion, formatDate, objectHasProperties,
equalsPartial, getObjectProperty, getResponse, parseJSON, loadModuleFromText, loadModuleFromFile,
getExternalConvertersDefinitions, removeNullPropertiesFromObject, toNetworkAddressHex, toSnakeCase,
parseEntityID, isEndpoint, isZHGroup, hours, minutes, seconds, validateFriendlyName, sleep,
sanitizeImageParameter, isAvailabilityEnabledForEntity, publishLastSeen, availabilityPayload,
getAllFiles, filterProperties, flatten, arrayUnique, clone, computeSettingsToChange,
};
+6 -6
View File
@@ -2,10 +2,8 @@ import yaml from 'js-yaml';
import fs from 'fs';
import equals from 'fast-deep-equal/es6';
// TODO: check all
export function read(file: string): KeyValue {
function read(file: string): KeyValue {
try {
/* eslint-disable-line */ // @ts-ignore
return yaml.load(fs.readFileSync(file, 'utf8'));
} catch (error) {
if (error.name === 'YAMLException') {
@@ -16,21 +14,23 @@ export function read(file: string): KeyValue {
}
}
export function readIfExists(file: string, default_?: KeyValue): KeyValue {
function readIfExists(file: string, default_?: KeyValue): KeyValue {
return fs.existsSync(file) ? read(file) : default_;
}
export function writeIfChanged(file: string, content: KeyValue): void {
function writeIfChanged(file: string, content: KeyValue): void {
const before = readIfExists(file);
if (!equals(before, content)) {
fs.writeFileSync(file, yaml.dump(content));
}
}
export function updateIfChanged(file: string, key: string, value: KeyValue): void {
function updateIfChanged(file: string, key: string, value: KeyValue): void {
const content = read(file);
if (content[key] !== value) {
content[key] = value;
writeIfChanged(file, content);
}
}
export default {read, readIfExists, updateIfChanged, writeIfChanged};
+121 -228
View File
@@ -1,25 +1,22 @@
import {Controller} from 'zigbee-herdsman';
import logger from './util/logger';
import * as settings from './util/settings';
import * as data from './util/data';
import assert from 'assert';
import * as utils from './util/utils';
import data from './util/data';
import utils from './util/utils';
import objectAssignDeep from 'object-assign-deep';
// @ts-ignore
import zigbeeHerdsmanConverters from 'zigbee-herdsman-converters';
// @ts-ignore
import stringify from 'json-stable-stringify-without-jsonify';
import Device from './model/device';
import Group from './model/group';
import * as ZHEvents from 'zigbee-herdsman/dist/controller/events';
import bind from 'bind-decorator';
export default class Zigbee {
private herdsman: Controller;
private eventBus: EventBus;
private resolvedEntitiesLookup: {[s: string]: Device | Group} = {};
private groupLookup: {[s: number]: Group} = {};
private deviceLookup: {[s: string]: Device} = {};
constructor(eventBus: EventBus) {
this.acceptJoiningDeviceHandler = this.acceptJoiningDeviceHandler.bind(this);
this.eventBus = eventBus;
}
@@ -39,8 +36,8 @@ export default class Zigbee {
databaseBackupPath: data.joinPath('database.db.backup'),
backupPath: data.joinPath('coordinator_backup.json'),
serialPort: {
baudRate: settings.get().advanced.baudrate,
rtscts: settings.get().advanced.rtscts,
baudRate: settings.get().serial.baudrate,
rtscts: settings.get().serial.rtscts,
path: settings.get().serial.port,
adapter: settings.get().serial.adapter,
},
@@ -52,9 +49,7 @@ export default class Zigbee {
acceptJoiningDeviceHandler: this.acceptJoiningDeviceHandler,
};
const herdsmanSettingsLog = objectAssignDeep({}, herdsmanSettings);
// @ts-ignore
herdsmanSettingsLog.network.networkKey = 'HIDDEN';
const herdsmanSettingsLog = objectAssignDeep({}, herdsmanSettings, {network: {networkKey: 'HIDDEN'}});
logger.debug(`Using zigbee-herdsman with settings: '${stringify(herdsmanSettingsLog)}'`);
let startResult;
@@ -68,63 +63,78 @@ export default class Zigbee {
this.herdsman.on('adapterDisconnected', () => this.eventBus.emitAdapterDisconnected());
this.herdsman.on('lastSeenChanged', (data: ZHEvents.LastSeenChangedPayload) => {
this.eventBus.emitLastSeenChanged({device: this.resolveEntity(data.device) as Device});
this.eventBus.emitLastSeenChanged({device: this.resolveDevice(data.device.ieeeAddr), reason: data.reason});
});
this.herdsman.on('permitJoinChanged', (data: ZHEvents.PermitJoinChangedPayload) => {
this.eventBus.emitPermitJoinChanged(data);
});
this.herdsman.on('deviceNetworkAddressChanged', (data: ZHEvents.DeviceNetworkAddressChangedPayload) => {
this.eventBus.emit('event', 'deviceNetworkAddressChanged', data); // TODO remove this event
this.eventBus.emitDeviceNetworkAddressChanged({device: this.resolveEntity(data.device) as Device});
const device = this.resolveDevice(data.device.ieeeAddr);
logger.debug(`Device '${device.name}' changed network address`);
this.eventBus.emitDeviceNetworkAddressChanged({device});
});
this.herdsman.on('deviceAnnounce', (data: ZHEvents.DeviceAnnouncePayload) => {
this.eventBus.emit('event', 'deviceAnnounce', data); // TODO remove this event
this.eventBus.emitDeviceAnnounce({device: this.resolveEntity(data.device) as Device});
const device = this.resolveDevice(data.device.ieeeAddr);
logger.debug(`Device '${device.name}' announced itself`);
this.eventBus.emitDeviceAnnounce({device});
});
this.herdsman.on('deviceInterview', (data: ZHEvents.DeviceInterviewPayload) => {
this.eventBus.emit('event', 'deviceInterview', data); // TODO remove this event
this.eventBus.emitDeviceInterview({device: this.resolveEntity(data.device) as Device, status: data.status});
const device = this.resolveDevice(data.device.ieeeAddr);
/* istanbul ignore if */ if (!device) return; // Prevent potential race
const d = {device, status: data.status};
this.logDeviceInterview(d);
this.eventBus.emitDeviceInterview(d);
});
this.herdsman.on('deviceJoined', (data: ZHEvents.DeviceJoinedPayload) => {
this.eventBus.emit('event', 'deviceJoined', data); // TODO remove this event
this.eventBus.emitDeviceJoined({device: this.resolveEntity(data.device) as Device});
const device = this.resolveDevice(data.device.ieeeAddr);
/* istanbul ignore if */ if (!device) return; // Prevent potential race
logger.info(`Device '${device.name}' joined`);
this.eventBus.emitDeviceJoined({device});
});
this.herdsman.on('deviceLeave', (data: ZHEvents.DeviceLeavePayload) => {
this.eventBus.emit('event', 'deviceLeave', data); // TODO remove this event
this.eventBus.emitDeviceLeave(data);
const name = settings.getDevice(data.ieeeAddr)?.friendly_name || data.ieeeAddr;
logger.warn(`Device '${name}' left the network`);
this.eventBus.emitDeviceLeave({ieeeAddr: data.ieeeAddr, name});
});
this.herdsman.on('message', (data: ZHEvents.MessagePayload) => {
this.eventBus.emit('event', 'message', data); // TODO remove this event
this.eventBus.emitDeviceMessage({...data, device: this.resolveEntity(data.device) as Device});
const device = this.resolveDevice(data.device.ieeeAddr);
logger.debug(`Received Zigbee message from '${device.name}', type '${data.type}', ` +
`cluster '${data.cluster}', data '${stringify(data.data)}' from endpoint ${data.endpoint.ID}` +
(data.hasOwnProperty('groupID') ? ` with groupID ${data.groupID}` : ``) +
(device.zh.type === 'Coordinator' ? `, ignoring since it is from coordinator` : ``));
if (device.zh.type === 'Coordinator') return;
this.eventBus.emitDeviceMessage({...data, device});
});
logger.info(`zigbee-herdsman started (${startResult})`);
logger.info(`Coordinator firmware version: '${stringify(await this.getCoordinatorVersion())}'`);
logger.debug(`Zigbee network parameters: ${stringify(await this.herdsman.getNetworkParameters())}`);
for (const device of this.getClients()) {
for (const device of this.devices(false)) {
// If a passlist is used, all other device will be removed from the network.
const passlist = settings.get().passlist.concat(settings.get().whitelist);
const blocklist = settings.get().blocklist.concat(settings.get().ban);
const passlist = settings.get().passlist;
const blocklist = settings.get().blocklist;
const remove = async (device: Device): Promise<void> => {
try {
await device.zh.removeFromNetwork();
} catch (error) {
logger.error(`Failed to remove '${device.ieeeAddr}' (${error.message})`);
}
};
if (passlist.length > 0) {
if (!passlist.includes(device.ieeeAddr)) {
logger.warn(`Device which is not on passlist connected (${device.ieeeAddr}), removing...`);
device.removeFromNetwork();
await remove(device);
}
} else if (blocklist.includes(device.ieeeAddr)) {
logger.warn(`Device on blocklist is connected (${device.ieeeAddr}), removing...`);
device.removeFromNetwork();
await remove(device);
}
}
// Check if we have to turn off the led
if (settings.get().serial.disable_led) {
await this.herdsman.setLED(false);
}
// Check if we have to set a transmit power
if (settings.get().experimental.hasOwnProperty('transmit_power')) {
const transmitPower = settings.get().experimental.transmit_power;
if (settings.get().advanced.hasOwnProperty('transmit_power')) {
const transmitPower = settings.get().advanced.transmit_power;
await this.herdsman.setTransmitPower(transmitPower);
logger.info(`Set transmit power to '${transmitPower}'`);
}
@@ -132,6 +142,27 @@ export default class Zigbee {
return startResult;
}
private logDeviceInterview(data: eventdata.DeviceInterview): void {
const name = data.device.name;
if (data.status === 'successful') {
logger.info(`Successfully interviewed '${name}', device has successfully been paired`);
if (data.device.definition) {
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 ` +
`'${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`);
}
} else if (data.status === 'failed') {
logger.error(`Failed to interview '${name}', device has not successfully been paired`);
} else { // data.status === 'started'
logger.info(`Starting interview of '${name}'`);
}
}
private generateNetworkKey(): number[] {
const key = Array.from({length: 16}, () => Math.floor(Math.random() * 255));
settings.set(['advanced', 'network_key'], key);
@@ -144,7 +175,7 @@ export default class Zigbee {
return panID;
}
async getCoordinatorVersion(): Promise<CoordinatorVersion> {
async getCoordinatorVersion(): Promise<zh.CoordinatorVersion> {
return this.herdsman.getCoordinatorVersion();
}
@@ -152,7 +183,11 @@ export default class Zigbee {
return this.herdsman.isStopping();
}
async getNetworkParameters(): Promise<NetworkParameters> {
async backup(): Promise<void> {
return this.herdsman.backup();
}
async getNetworkParameters(): Promise<zh.NetworkParameters> {
return this.herdsman.getNetworkParameters();
}
@@ -182,70 +217,70 @@ export default class Zigbee {
}
if (device && permit) {
await this.herdsman.permitJoin(permit, device.zhDevice, time);
await this.herdsman.permitJoin(permit, device.zh, time);
} else {
await this.herdsman.permitJoin(permit, undefined, time);
}
}
private addDeviceToResolvedEntitiesLookup(ieeeAddr: string): Device {
if (!this.resolvedEntitiesLookup[ieeeAddr]) {
@bind private resolveDevice(ieeeAddr: string): Device {
if (!this.deviceLookup[ieeeAddr]) {
const device = this.herdsman.getDeviceByIeeeAddr(ieeeAddr);
/* istanbul ignore else */
if (device) this.resolvedEntitiesLookup[ieeeAddr] = new Device(device);
device && (this.deviceLookup[ieeeAddr] = new Device(device));
}
return this.resolvedEntitiesLookup[ieeeAddr] as Device;
const device = this.deviceLookup[ieeeAddr];
if (device && !device.zh.isDeleted) {
device.ensureInSettings();
return device;
}
}
private addGroupToResolvedEntitiesLookup(groupID: number): Group {
if (!this.resolvedEntitiesLookup[groupID]) {
let group = this.herdsman.getGroupByID(groupID);
// Legacy: previously zigbee-herdsman did not keep track of groups, therefore create it when published to
// note that the group is in the configuration.yaml already.
if (group == null) group = this.herdsman.createGroup(groupID);
this.resolvedEntitiesLookup[groupID] = new Group(group);
private resolveGroup(groupID: number): Group {
const group = this.herdsman.getGroupByID(Number(groupID));
if (group && !this.groupLookup[groupID]) {
this.groupLookup[groupID] = new Group(group, this.resolveDevice);
}
return this.resolvedEntitiesLookup[groupID] as Group;
return this.groupLookup[groupID];
}
resolveEntity(key: ZHDevice | string): Device | Group {
const ID = typeof key === 'string' ? key : key.ieeeAddr;
const entitySettings = settings.getEntity(ID);
if (!entitySettings && !(typeof key === 'object' && key.type === 'Coordinator')) return undefined;
resolveEntity(key: string | number | zh.Device): Device | Group {
if (typeof key === 'object') {
return this.addDeviceToResolvedEntitiesLookup(key.ieeeAddr);
return this.resolveDevice(key.ieeeAddr);
} else if (typeof key === 'string' && key.toLowerCase() === 'coordinator') {
return this.resolveDevice(this.herdsman.getDevicesByType('Coordinator')[0].ieeeAddr);
} else {
return entitySettings.type === 'device' ?
this.addDeviceToResolvedEntitiesLookup(entitySettings.ID as string) :
this.addGroupToResolvedEntitiesLookup(entitySettings.ID as number);
const settingsDevice = settings.getDevice(key.toString());
if (settingsDevice) return this.resolveDevice(settingsDevice.ID);
const groupSettings = settings.getGroup(key);
if (groupSettings) {
const group = this.resolveGroup(groupSettings.ID);
// If group does not exist, create it (since it's already in configuration.yaml)
return group ? group : this.createGroup(groupSettings.ID);
}
}
}
getClients(): Device[] {
return this.herdsman.getDevices().filter((device) => device.type !== 'Coordinator')
.map((d) => this.resolveEntity(d) as Device).filter((d) => d);
}
getFirstCoordinatorEndpoint(): ZHEndpoint {
firstCoordinatorEndpoint(): zh.Endpoint {
return this.herdsman.getDevicesByType('Coordinator')[0].endpoints[0];
}
getGroups(): Group[] {
return this.herdsman.getGroups().map((g) => this.addGroupToResolvedEntitiesLookup(g.groupID)).filter((g) => g);
groups(): Group[] {
return this.herdsman.getGroups().map((g) => this.resolveGroup(g.groupID));
}
getDevices(): Device[] {
devices(includeCoordinator=true): Device[] {
return this.herdsman.getDevices()
.map((d) => this.addDeviceToResolvedEntitiesLookup(d.ieeeAddr)).filter((d) => d);
.map((d) => this.resolveDevice(d.ieeeAddr))
.filter((d) => includeCoordinator || d.zh.type !== 'Coordinator');
}
private async acceptJoiningDeviceHandler(ieeeAddr: string): Promise<boolean> {
@bind private async acceptJoiningDeviceHandler(ieeeAddr: string): Promise<boolean> {
// If passlist is set, all devices not on passlist will be rejected to join the network
const passlist = settings.get().passlist.concat(settings.get().whitelist);
const blocklist = settings.get().blocklist.concat(settings.get().ban);
const passlist = settings.get().passlist;
const blocklist = settings.get().blocklist;
if (passlist.length > 0) {
if (passlist.includes(ieeeAddr)) {
logger.info(`Accepting joining device which is on passlist '${ieeeAddr}'`);
@@ -275,6 +310,10 @@ export default class Zigbee {
return this.herdsman.touchlinkFactoryReset(ieeeAddr, channel);
}
async addInstallCode(installCode: string): Promise<void> {
await this.herdsman.addInstallCode(installCode);
}
async touchlinkIdentify(ieeeAddr: string, channel: number): Promise<void> {
await this.herdsman.touchlinkIdentify(ieeeAddr, channel);
}
@@ -283,163 +322,17 @@ export default class Zigbee {
return this.herdsman.touchlinkScan();
}
createGroup(groupID: number): Group {
this.herdsman.createGroup(groupID);
return this.addGroupToResolvedEntitiesLookup(groupID);
createGroup(ID: number): Group {
this.herdsman.createGroup(ID);
return this.resolveGroup(ID);
}
deviceByNetworkAddress(networkAddress: number): Device {
const device = this.herdsman.getDeviceByNetworkAddress(networkAddress);
return device && this.addDeviceToResolvedEntitiesLookup(device.ieeeAddr);
return device && this.resolveDevice(device.ieeeAddr);
}
// TODO remove all legacy below
createGroupLegacy(groupID: number): ZHGroup {
return this.herdsman.createGroup(groupID);
}
getGroupsLegacy(): ZHGroup[] {
return this.herdsman.getGroups();
}
getGroupByIDLegacy(ID: number): ZHGroup {
return this.herdsman.getGroupByID(ID);
}
getDevicesByTypeLegacy(type: 'Coordinator' | 'Router' | 'EndDevice'): ZHDevice[] {
return this.herdsman.getDevicesByType(type);
}
getClientsLegacy(): ZHDevice[] {
return this.herdsman.getDevices().filter((device) => device.type !== 'Coordinator');
}
getDevicesLegacy(): ZHDevice[] {
return this.herdsman.getDevices();
}
async permitJoinLegacy(permit: boolean, resolvedEntity: ResolvedDevice, time: number=undefined): Promise<void> {
if (permit) {
/* istanbul ignore next */
logger.info(`Zigbee: allowing new devices to join${resolvedEntity ? ` via ${resolvedEntity.name}` : ''}.`);
} else {
logger.info('Zigbee: disabling joining new devices.');
}
/* istanbul ignore next */
if (resolvedEntity && permit) {
/* istanbul ignore next */
await this.herdsman.permitJoin(permit, resolvedEntity.device, time);
} else {
await this.herdsman.permitJoin(permit, undefined, time);
}
}
/* eslint-disable-next-line */
resolveEntityLegacy(key: any): any {
assert(
typeof key === 'string' || typeof key === 'number' ||
key.constructor.name === 'Device' || key.constructor.name === 'Group' ||
key.constructor.name === 'Endpoint',
`Wrong type '${typeof key}'`,
);
/* eslint-disable-next-line */
const getEndpointName = (endpointNames: any, endpoint: any) => {
return endpoint ?
utils.getKey(endpointNames, endpoint.ID, null, ((v) => v === 'default' ? null : v)) : null;
};
const deviceOptions = settings.get().device_options;
/* istanbul ignore else */
if (typeof key === 'string' || typeof key === 'number') {
if (typeof key === 'number') {
key = key.toString();
}
if (typeof key === 'string' && key.toLowerCase() === 'coordinator') {
const coordinator = this.getDevicesByTypeLegacy('Coordinator')[0];
return {
type: 'device',
device: coordinator,
endpoint: coordinator.getEndpoint(1),
settings: {friendlyName: 'Coordinator'},
name: 'Coordinator',
endpointName: null,
};
}
/* eslint-disable-next-line */
let endpointKey: any = utils.endpointNames.find((p) => key.endsWith(`/${p}`));
const keyEndpointByNumber = new RegExp(`.*/([0-9]*)$`);
const endpointByNumber = key.match(keyEndpointByNumber);
if (!endpointKey && endpointByNumber) {
endpointKey = Number(endpointByNumber[1]);
}
if (endpointKey) {
key = key.replace(`/${endpointKey}`, '');
}
const entity = settings.getEntity(key);
if (!entity) {
return null;
} else if (entity.type === 'device') {
/* eslint-disable-line */ // @ts-ignore
const device = this.herdsman.getDeviceByIeeeAddr(entity.ID);
if (!device) {
return null;
}
const definition = zigbeeHerdsmanConverters.findByDevice(device);
const endpointNames = definition && definition.endpoint ? definition.endpoint(device) : null;
let endpoint;
if (endpointKey) {
if (endpointByNumber) {
endpoint = device.getEndpoint(endpointKey);
} else {
assert(definition != null, `Endpoint name '${endpointKey}' is given but device is unsupported`);
assert(endpointNames != null,
`Endpoint name '${endpointKey}' is given but no endpoints defined`);
const endpointID = endpointNames[endpointKey];
assert(endpointID, `Endpoint name '${endpointKey}' is given but device has no such endpoint`);
endpoint = device.getEndpoint(endpointID);
}
} else if (endpointNames && endpointNames['default']) {
endpoint = device.getEndpoint(endpointNames['default']);
} else {
endpoint = device.endpoints[0];
}
return {
type: 'device', device, endpoint, settings: {...deviceOptions, ...entity},
name: entity.friendlyName, definition,
endpointName: getEndpointName(endpointNames, endpoint),
};
} else {
/* eslint-disable-line */ // @ts-ignore
let group = this.getGroupByIDLegacy(entity.ID);
/* istanbul ignore if */ // @ts-ignore
if (!group) group = this.createGroupLegacy(entity.ID);
return {type: 'group', group, settings: {...deviceOptions, ...entity}, name: entity.friendlyName};
}
} else if (key.constructor.name === 'Device' || key.constructor.name === 'Endpoint') {
const device = key.constructor.name === 'Endpoint' ? key.getDevice() : key;
const setting = settings.getEntity(device.ieeeAddr);
const definition = zigbeeHerdsmanConverters.findByDevice(device);
const name = setting ? setting.friendlyName :
(device.type === 'Coordinator' ? 'Coordinator' : device.ieeeAddr);
const endpointNames = definition && definition.endpoint ? definition.endpoint(device) : null;
let endpoint;
if (key.constructor.name === 'Endpoint') endpoint = key;
else if (endpointNames && endpointNames['default']) endpoint = device.getEndpoint(endpointNames['default']);
else endpoint = device.endpoints[0];
return {
type: 'device', definition, name, device, endpoint, settings: {...deviceOptions, ...(setting || {})},
endpointName: getEndpointName(endpointNames, endpoint),
};
} else { // Group
const setting = settings.getEntity(key.groupID);
return {
type: 'group',
group: key,
settings: {...deviceOptions, ...(setting || {})},
name: setting ? setting.friendlyName : key.groupID,
};
}
groupByID(ID: number): Group {
return this.resolveGroup(ID);
}
}
-23357
View File
File diff suppressed because it is too large Load Diff
+16423
View File
File diff suppressed because it is too large Load Diff
+39 -35
View File
@@ -1,6 +1,6 @@
{
"name": "zigbee2mqtt",
"version": "1.21.2",
"version": "1.32.1",
"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": "^10 || ^12 || ^14 || ^15 || ^16"
"node": "^14 || ^16 || ^18 || ^19 || ^20"
},
"keywords": [
"xiaomi",
@@ -35,53 +35,57 @@
},
"homepage": "https://koenkk.github.io/zigbee2mqtt",
"dependencies": {
"ajv": "^8.6.3",
"ajv": "^8.12.0",
"bind-decorator": "^1.0.11",
"connect-gzip-static": "2.1.1",
"debounce": "^1.2.1",
"deep-object-diff": "^1.1.0",
"deep-object-diff": "^1.1.9",
"fast-deep-equal": "^3.1.3",
"finalhandler": "^1.1.2",
"finalhandler": "^1.2.0",
"git-last-commit": "^1.0.1",
"humanize-duration": "^3.27.0",
"humanize-duration": "^3.28.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.29.1",
"mqtt": "4.2.8",
"moment": "^2.29.4",
"mqtt": "4.3.7",
"object-assign-deep": "^0.4.0",
"rimraf": "^3.0.2",
"semver": "^7.3.5",
"serve-static": "^1.14.1",
"source-map-support": "^0.5.20",
"winston": "^3.3.3",
"winston-syslog": "^2.4.4",
"ws": "^8.2.2",
"zigbee-herdsman": "0.13.149",
"zigbee-herdsman-converters": "14.0.261",
"zigbee2mqtt-frontend": "0.6.12"
"rimraf": "^5.0.1",
"semver": "^7.5.1",
"source-map-support": "^0.5.21",
"uri-js": "^4.4.1",
"winston": "^3.9.0",
"winston-syslog": "^2.7.0",
"winston-transport": "^4.5.0",
"ws": "^8.13.0",
"zigbee-herdsman": "0.16.0",
"zigbee-herdsman-converters": "15.33.1-hotfix.0",
"zigbee2mqtt-frontend": "0.6.129"
},
"devDependencies": {
"@babel/core": "^7.15.5",
"@babel/plugin-proposal-decorators": "^7.15.4",
"@babel/preset-env": "^7.15.6",
"@babel/preset-typescript": "^7.15.0",
"@babel/core": "^7.22.5",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-proposal-decorators": "^7.22.5",
"@babel/preset-env": "^7.22.5",
"@babel/preset-typescript": "^7.22.5",
"@types/debounce": "^1.2.1",
"@types/finalhandler": "^1.1.1",
"@types/humanize-duration": "^3.25.1",
"@types/jest": "^27.0.2",
"@types/js-yaml": "^4.0.3",
"@types/finalhandler": "^1.2.0",
"@types/humanize-duration": "^3.27.1",
"@types/jest": "^29.5.2",
"@types/js-yaml": "^4.0.5",
"@types/object-assign-deep": "^0.4.0",
"@types/serve-static": "^1.13.10",
"@types/ws": "^8.2.0",
"@typescript-eslint/eslint-plugin": "^4.31.2",
"@typescript-eslint/parser": "^4.31.2",
"babel-jest": "^27.2.2",
"eslint": "^7.32.0",
"@types/rimraf": "^3.0.2",
"@types/ws": "8.5.4",
"@typescript-eslint/eslint-plugin": "^5.59.9",
"@typescript-eslint/parser": "^5.59.9",
"babel-jest": "^29.5.0",
"eslint": "^8.42.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-jest": "^24.4.2",
"jest": "^27.2.2",
"eslint-plugin-jest": "^27.2.1",
"jest": "^29.5.0",
"tmp": "^0.2.1",
"typescript": "^4.4.3"
"typescript": "^5.1.3"
},
"jest": {
"coverageThreshold": {
+15
View File
@@ -0,0 +1,15 @@
{
"packages": {
".": {
"changelog-path": "CHANGELOG.md",
"release-type": "node",
"bump-minor-pre-major": false,
"bump-patch-for-minor-pre-major": false,
"draft": false,
"include-v-in-tag": false,
"prerelease": false,
"skip-github-release": true
}
},
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
}
-35
View File
@@ -1,35 +0,0 @@
const HomeAssistant = require('../lib/extension/homeassistant');
const stringify = require('json-stable-stringify-without-jsonify');
const homeassistant = new HomeAssistant(null, null, null, null, {on: () => {}});
const mapping = Object.entries(homeassistant._getMapping());
mapping.sort((a, b) => {
if (a[0] > b[0]) {
return -1;
}
if (b[0] > a[0]) {
return 1;
}
return 0;
});
for (const map of mapping) {
map[1].sort((a, b) => {
return JSON.stringify(a).localeCompare(JSON.stringify(b));
});
const toDelete = [];
for (const entry of map[1]) {
if (entry.type === 'light') {
if (entry.discovery_payload.brightness === false) delete entry.discovery_payload.brightness;
if (entry.discovery_payload.color_temp === false) delete entry.discovery_payload.color_temp;
if (entry.discovery_payload.xy === false) delete entry.discovery_payload.xy;
if (entry.discovery_payload.hs === false) delete entry.discovery_payload.hs;
} else if (entry.object_id === 'linkquality') {
toDelete.push(entry);
}
}
toDelete.forEach((d) => map[1].splice(map[1].indexOf(d), 1));
console.log(`${map[0]} - ${stringify(map[1])}`);
}
+116
View File
@@ -0,0 +1,116 @@
/* eslint max-len: 0 */
const path = require('path');
const fs = require('fs');
const process = require('process');
const {execSync} = require('child_process');
const zhc = require('zigbee-herdsman-converters');
const z2mTillVersion = process.argv[2];
const zhcTillVersion = process.argv[3];
const zhTillVersion = process.argv[4];
const changelogs = [
{tillVersion: z2mTillVersion, project: 'koenkk/zigbee2mqtt',
contents: fs.readFileSync(path.join(__dirname, '..', 'CHANGELOG.md'), 'utf-8').split('\n')},
{tillVersion: zhcTillVersion, project: 'koenkk/zigbee-herdsman-converters',
contents: fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'zigbee-herdsman-converters', 'CHANGELOG.md'), 'utf-8').split('\n')},
{tillVersion: zhTillVersion, project: 'koenkk/zigbee-herdsman',
contents: fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'zigbee-herdsman', 'CHANGELOG.md'), 'utf-8').split('\n')},
];
const releaseRe = /## \[(.+)\]/;
const changes = {features: [], fixes: [], detect: [], add: [], error: []};
let context = null;
const changeRe = [
/^\* (\*\*(.+):\*\*)?(.+)\((\[#\d+\]\(.+\))\) \(\[.+\]\(https:.+\/(.+)\)\)$/,
/^\* (\*\*(.+):\*\*)?(.+)(https:\/\/github\.com.+) \(\[.+\]\(https:.+\/(.+)\)\)$/,
/^\* (\*\*(.+):\*\*)?(.+)() \(\[.+\]\(https:.+\/(.+)\)\)$/,
];
let commitUserLookup = {};
const commitUserFile = path.join(__dirname, 'commit-user-lookup.json');
if (fs.existsSync(commitUserFile)) {
commitUserLookup = JSON.parse(fs.readFileSync(commitUserFile, 'utf8'));
}
for (const changelog of changelogs) {
for (const line of changelog.contents) {
const releaseMatch = line.match(releaseRe);
const changeMatch = changeRe.map((re) => line.match(re)).find((e) => e);
if (releaseMatch) {
if (releaseMatch[1] === changelog.tillVersion) {
break;
}
} else if (line === '### Features') {
context = 'features';
} else if (line === '### Bug Fixes') {
context = 'fixes';
} else if (line.startsWith('* **ignore:**')) {
continue;
} else if (changeMatch) {
let localContext = changeMatch[2] ? changeMatch[2] : context;
if (!changes[localContext]) throw new Error(`Unknown context: ${localContext}`);
const commitUserKey = `${changelog.project}-${changeMatch[5]} `;
let user = commitUserKey in commitUserLookup ? commitUserLookup[commitUserKey] :
execSync(`curl -s https://api.github.com/repos/${changelog.project}/commits/${changeMatch[5]} | jq -r '.author.login'`).toString().trim();
if (user !== 'null') commitUserLookup[commitUserKey] = user;
const messages = [];
let message = changeMatch[3].trim();
if (message.endsWith('.')) message = message.substring(0, message.length - 1);
const otherUser = message.match(/\[@(.+)\]\(https:\/\/github.com\/.+\)/) || message.match(/@(.+)/);
if (otherUser) {
user = otherUser[1];
message = message.replace(otherUser[0], '');
}
if (localContext === 'add') {
for (const model of message.split(',')) {
const definition = zhc.definitions.find((d) => d.model === model.trim());
if (definition) {
messages.push(`\`${definition.model}\` ${definition.vendor} ${definition.description}`);
} else {
changes['error'].push(`${line} (model '${model}' does not exist)`);
}
}
} else {
messages.push(message);
}
let issue = changeMatch[4].trim();
if (issue && !issue.startsWith('[#')) issue = `[#${issue.split('/').pop()}](${issue})`;
if (!issue) {
issue = '_NO_ISSUE_';
localContext = 'error';
}
messages.forEach((m) => changes[localContext].push(`- ${issue} ${m} (@${user})`));
} else if (line === '# Changelog' || !line) {
continue;
} else {
throw new Error(`Unmatched line: ${line}`);
}
}
}
let result = '';
const names = [
['features', 'Improvements'],
['fixes', 'Fixes'],
['add', 'New supported devices'],
['detect', 'Fixed device detections'],
['error', 'Changelog generator error'],
];
for (const name of names) {
result += `# ${name[1]}\n`;
if (name[0] === 'add') {
result += `This release adds support for ${changes['add'].length} devices: \n`;
}
changes[name[0]].forEach((e) => result += `${e}\n`);
result += '\n';
}
fs.writeFileSync(commitUserFile, JSON.stringify(commitUserLookup), 'utf-8');
console.log(result.trim());
+4 -2
View File
@@ -5,6 +5,8 @@ const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
for (const line of text.split('\n')) {
const model = zigbeeHerdsmanConverters.devices.find((d) => d.model === line);
console.log(`- \`${line}\` ${model.vendor} ${model.description}`);
const [modelNumber, user] = line.split(',').map((t) => t.trim());
const model = zigbeeHerdsmanConverters.devices.find((d) => d.model === modelNumber);
if (!model) throw new Error(`${line} does not exist`);
console.log(`- \`${modelNumber}\` ${model.vendor} ${model.description} (${user})`);
}
+22
View File
@@ -0,0 +1,22 @@
const assert = require('assert');
const vm = require('vm');
const fs = require('fs');
const path = require('path');
const filename = process.argv[2];
const moduleCode = fs.readFileSync(filename);
const moduleFakePath = path.join(__dirname, 'externally-loaded.js');
const sandbox = {
require: require,
module: {},
console,
setTimeout,
clearTimeout,
setInterval,
clearInterval,
setImmediate,
clearImmediate,
};
vm.runInNewContext(moduleCode, sandbox, moduleFakePath);
const converter = sandbox.module.exports;
assert(!converter.toZigbee.includes(undefined));
assert(!converter.fromZigbee.includes(undefined));
+31
View File
@@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIUU70Q1tbGP071yeQ8EcHd/0rxtnwwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMjEyMDMwMDA1MTBaFw0yMzAx
MDIwMDA1MTBaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQDR6bYQPgkfWlGQq19U2nMsAo0RZR7wSUuFS0MFpbQS
2Rr5x/4FwwKhTMOlfr6H2Xo/5pQvGOlhFE69j1HxQRooWDAswnscMv/9pb+BUqoE
7J29SEndIpmCaI0c3ejZsu4Nq8DxGod/RsQhJdbDU+hkw8qARJ0JjnTb+aN1KH8g
J3KeSMTOL3guBVZQujO0Unbjv47yBIFwi0IihR18aoRaxinb94Aqf92HcjZN++WU
gq7/2Qlm3tF5uJwuNtG/DmzReSblRcUgzPri7qIhb9z51gFNdNX6cTH3yNoF/KtT
XwEArvvsAyMCYuC6w8KI7WNmPI6yxSksgHdcNPlUVZYquLD+z2kYAdIByiyHmIpm
OH3jVN7lRJ6xITOGmyNktfRuSUi2GzECAOWIGpDD/5/gK1pacbOXpJWqVrTjUIAN
BrQf496CbabVgwaQEvq1QJJbuMbpma21/IlS6NpZS7vm/q1ojNsbEB6PYkSsBTzP
iZiiaomjhV2w5yIp5Zn8xyMFVKLb+SEDQBtZZ6CErmbgqp4B3U2b4k5LippLmZZK
pSk7BOuMpsZK8FIGS4KlANFfGVs79y6N+Jxue2IO9pRcpcpbqOXty4662BsPKInw
dKxKTr6TcTe0gEq1fpf1npYwcZ9t36LMJ7L51TbaaJ5sEsXe5QNOOOUgSwPqWmrE
lwIDAQABo1MwUTAdBgNVHQ4EFgQUYD/x9cCuGJgLUPkYLIVa8uvseBkwHwYDVR0j
BBgwFoAUYD/x9cCuGJgLUPkYLIVa8uvseBkwDwYDVR0TAQH/BAUwAwEB/zANBgkq
hkiG9w0BAQsFAAOCAgEAI+gp+8qqO4anj8NHgjJvXhrETLPJIeLuBp/cjF9HA2IL
oAd5rXWPnQ6qgh3q/HazILgIHwI0wSrhVtRlprgGIxNa59cD3RcZCtqFSWIqhAqZ
JnAye3bnGU/KahazQX3oP+y564b/QZvwpJDKC1EoMv1wdpXND2NVn4HwaBc7Tu/d
SX/Tr6k+e32K8mwE+00/yf2rLrmWmNjWtP0f7zRmH3j1+ovktk7UoVhjKXfhqMUE
pWdhPZIXQJ65JNMGKdeFI2ULQY9IbXHeWQUgBCyhTciMvm2XZzopk7rxrh9NZopS
pAlCrWBkOZrs6hfZrus9FOzwSkeIEY7NnhNk05kslERJxjkoFytp7mDd4mTKCVW0
dKc7G1hZWYfO3oe0Zjxi0ragyReNKtyL1BqMbDKJmxHl91iPbdueWix1xxhNFakH
l3eOzfa/DEixvzxy3hzCcvagNb5RsXFVMQXQTgnIW/5GcG5VVB2/o7KidPL3XtqR
Hc/0ebQvloGXgqJbyTXG9EInkwB35wXDQuHgwt1PhwzP1yfp2BCvDsQWrR3IxnBT
DO1o2RpsurgCgUpd0vcW17DePLNfprrhhU2uTPn2bNcWdT/rZmEImkNaA29Rku+3
SZQJZWIx4s2FMdVdAv6ZauROStTY/WayudfcFfP+hYvJ5/ccQ9Gkb7vrGUssFRw=
-----END CERTIFICATE-----
+54
View File
@@ -0,0 +1,54 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIJnDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIrNH7wPLdpbUCAggA
MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECN97bYsRcF6xBIIJSLVWpqTzdlNO
5J7kOrNcdbbg+SSEYbtT+G9gtwtkI8KO+xzYQZ/fdqmXgNqf9AQmikwDWQkoS4gC
qq6S+iKYn7Y8ZNZ/xu7gWW6L6uNZ3LTe/vkpenU6kmZrDysFTW5b7iH2VmMc/2h/
J3o3unxeMLMVSaoDG3MG08qj53yTmetTkZZZc4fgf6vK9/4fTJhdXu6n+1COBLBI
KbqbTRKnai3CNiYzz3UHyp9CGx1nX06beTJW8bYJUld9cJz+ozLNSV78oCkcUfwG
yR7p/6+86cWor5SwYG1wEm6w2bXrIJILTUdT92gXC8fhc/B6Id8hcuLIHcvFQ/CP
31y4Qwea3vY6b1TeRbqS4/fYOYajsiZi9ps0Meg55qMJj/UuNW2iLlR7PVsDXmPO
33X6cpEVyIlNCJzRREotzSoGmcXjQ5Dgd1LkxZPb163NGkRoJT4GcKCtQxYIyIzy
qyU0YF7I5LPKtNt1IPURHHerW42jM2jFlISxUixgl5Pj3WrmXeEmXq7htItn6X/q
tVEVUf6srs3aos2Ohx7Ji9PVDNuMGGGKxleCM6fU9ax/gYuk5r6PMYGPsD26xuZ0
A9WbFFFI6N1V+M3YsJJdxevZRV1NB3RymYGwFoZFDOKLb5DYJmPP3VVN/DsMNPkw
qaue7rinxBdLi8J6Itusiscg5pCyYQaujXtkK/bi6c1HCld4RvWCDgeU52SWRdJ9
2Np2629AeBvBfHP1/DsaUsyQPJAgDJk0HXoJyqstoALkRZ6ICoi1833LW/JGIWOv
xnl3JZ2q12aqb2OWxiBUKspnt2y5C2KpQJdmuEL68lAKLg+fzLi/jhJG1bwzWo79
I5G+QeV9+kI3vAIYHu331a+Yevs2p84sIumDGFSCSHAaWMmsrizJ4eKl5NnwZHS+
xUvcW+ISRKNRm4DWAL/nlwBJWMhCz+nsTKYvE1JO/IH2P5xxtD6bTjJ3YkVAHkBZ
wfApCBxdiQurEiabj06C4OCcNRkqJ/GwLrdxiwTCAt2hd7Dn+ttLo3Fi6iPRae8c
ZtWsCWaFObZ1QBeu7lKm+ev/Ab2SHtp6QMfrArNsrdIxSH1+Sm4DgfHmTmte2Tse
KFHy5Rwokst+Y18zjp3auJ3+NTTr9D1x2JCdQ6wkh66Pyd2FBLqRrMI1fHq9t9P+
r4DQm9I6FbgALm9yJ2QxV4ytwFGsowAye3vF+hfO5OjAScrG5DrxASRXlnp59zpd
+vMLk6rzcfiHzaJzYc/SOU9ubKN93aJmlL0Zgf1gg54oGnmPbEJeqQzeKVBtisro
FrNUPEFx5QOaqjVipPOpZfpTb0oaUpBdp4RpeI7W3Gb/dsmsRj7ItxvM6QxFdd5e
XfDZEYaUJRwSG3lHg9URWwSmfRWV81BhEuwvji4rNIEQ32VMsi2WWWPLOYYKGIw/
yE56xWBw7K78XwVx/b5yoNHBNjVNyjpNZ7jXkx6zdOjbqEpXJP+WfgVkYwlhlvAq
cCDbrF7jVWFqTV9tZNxlbaFlS8f17vld8DmmJ29Lo+WecsOo3M9n2Z6i79u7Xg2O
ip0V2Textq8rrKOwC1TLCRoQLypENO/cwdaF5amVkoXYDlY0rQtxf65Xe9bnJLrM
1G+tG8NZQu/bvW0mgrm0XHt2i6yfjn6sXEOY5VEC2+gRUriQ+GCETgzMe6ukb26/
VT38Va10SIu+xXwf/GXTwLskOO0AyWLBopTpL5q6faM4o/USX+Xl98sQVNQurHMS
xSQtpL6YPhQrmW7q5Q57OVWy70kEU8RpOJx6I7uyoUDz2g3bjmpIPpQX55vhofXK
3Xn76Ce9TyiVol71mPtBT1tYzi3ytZh/EYm2Wq3lEhbMT7zjw3T1Ypb4+q12VdCy
0Y9swKsQdhRCT6FwcCFSpCc2TzCCJCR6mChTe+z6rNm5BS2M6BaYcrX8PP82rZ8k
L0l3ymSLqSJ3J6Q+fdKhRq8EbCq6ngi31Q/l7fPHkW5VAJyowPRayD7UM09n6ou+
sg/kfQB8nh46uVxRRDpyUsrfhtIEQec2xrU17NmCr2VlAeOjWBn8lE/J1Gfn8WyV
05mY1aDGcRk/x02+74UujyuozYMfIVDXHpxp8YteTnBfiKVrRq+Aa9zVX9bkyagp
SJUTpF7lqMmiuvyZnG2xfLDfILR/zTRIRrm+ZIJLHeVQJtibOJ1llxE6kRvKx6zp
La47YCHKH+rLDTlNgrE5K/JtQgOKc+7bGQ6SCszf8OZWi5B2+Z+Ht36c3xQk9IOW
cjNpvjZxpiw6L1MhlQPE/wxz2JkOlKjJN0MmLscIyu15X+W/BvHwB8d11CRKUUiE
pV7OUoW2Ol1IgFHrEY33IhxwtyKtAKfQCiORarnwk/GvcD3TqmnzUeQdscdww4BI
wsO3c3aGr+wvU3P/8oFAoffD1KRihtoZ6CB347JcnaeEPlOS13a28JsJCOtjMxy9
wBH6UgzTwo04lDAStST+aIT4TaU4dlWmsAEQTpE8sa4DjpFtzK/ll+Wg2sA/SYaA
uAugsAOnTb1pkgk43GoYfc2fZCu1g9b/L3mZrE62W8HDfm67PWT65XMJziy/hcCy
DbXMAWfohLdePKDG7J0QQI8xcTN9TT5q3ZCglI9Xf7PE/qrHROdRgF8c8zyvB9dG
AVx96Z7akBoveqlFS44MnxIOGI0AzV8MrVbN51W6RHNyg/6UTL50/8zHmRWZj+iO
tfqiW+CtSHacYPy+Tcuft30PWZZkQDGphbRxaBRB00Fb+Bo57epDw4JS+4wRru33
2RHaVzQmnXwR6kP85x4uAR53UdgMgfK4cyylnFqGDlLFrHZfauFHg3kMIwqgVIVA
uMRrWC9XXU11j6NnP9J6Y64uSrjsh6eZOrtJIwA16U2r3PMMbn1hIdADrKu1MsPt
bkNsnPQUKJyX8DbRX45kje5ssUerbkxhvewvHQJlTyDxtgk/pGHOyKXoT+5vQEj9
pwHOrXi+QWidzi2vfRoNQJ0wXJr39dDTn0hVdsgFo2PsTh98W9cocVQfJnCL4Djs
Zlsp/BJOwlb2e3Q28E6pNyT+N63ZYEO9zLV/WmCB/eV2uMVoKibX3KszYkn0jAs0
RBytgnB8YW3n9RyorShchCKJzdjzR7nw79/lSLgDFE6W6Xh3VaGM07hz0vYWfaui
yvytkM9LPan2/GJM4zLFeA==
-----END ENCRYPTED PRIVATE KEY-----
+1 -1
View File
@@ -4,7 +4,7 @@ class Example {
this.mqtt.publish('example/extension', 'call from constructor')
}
onMQTTConnected() {
start() {
this.mqtt.publish('example/extension', 'test')
}
}
+281 -349
View File
@@ -1,415 +1,347 @@
const data = require('./stub/data');
const logger = require('./stub/logger');
const stringify = require('json-stable-stringify-without-jsonify');
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
zigbeeHerdsman.returnDevices.push('0x000b57fffec6a5b3');
zigbeeHerdsman.returnDevices.push('0x00124b00120144ae');
zigbeeHerdsman.returnDevices.push('0x0017880104e45553');
zigbeeHerdsman.returnDevices.push('0x0017880104e45517');
zigbeeHerdsman.returnDevices.push('0x000b57fffec6a5b2');
zigbeeHerdsman.returnDevices.push('0x0017880104e45535');
zigbeeHerdsman.returnDevices.push('0x0017880104e45521');
zigbeeHerdsman.returnDevices.push('0x0017880104e45525');
const MQTT = require('./stub/mqtt');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = require('./lib/flushPromises');
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {});
import data from './stub/data';
import logger from './stub/logger';
import MQTT from './stub/mqtt';
import zigbeeHerdsman from './stub/zigbeeHerdsman';
import utils from '../lib/util/utils';
import * as settings from '../lib/util/settings';
import Controller from '../lib/controller';
import flushPromises from './lib/flushPromises';
import stringify from 'json-stable-stringify-without-jsonify';
const mocks = [MQTT.publish, logger.warn, logger.debug];
const devices = zigbeeHerdsman.devices;
zigbeeHerdsman.returnDevices.push(
...[devices.bulb_color.ieeeAddr, devices.bulb_color_2.ieeeAddr, devices.coordinator.ieeeAddr, devices.remote.ieeeAddr])
describe('Availability', () => {
let controller;
let extension;
let resetExtension = async () => {
await controller.enableDisableExtension(false, 'AvailabilityLegacy');
await controller.enableDisableExtension(true, 'AvailabilityLegacy');
extension = controller.extensions.find((e) => e.constructor.name === 'AvailabilityLegacy');
await controller.enableDisableExtension(false, 'Availability');
await controller.enableDisableExtension(true, 'Availability');
}
const advancedTime = async (value) => {
jest.setSystemTime(Date.now() + value);
jest.advanceTimersByTime(value);
await flushPromises();
}
beforeAll(async () => {
jest.useFakeTimers();
jest.spyOn(utils, 'sleep').mockImplementation(async (seconds) => {});
jest.useFakeTimers('modern');
settings.reRead();
settings.set(['availability'], true);
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
});
beforeEach(async () => {
jest.useFakeTimers('modern').setSystemTime(utils.minutes(1));
data.writeDefaultConfiguration();
settings.reRead();
settings.set(['advanced', 'availability_timeout'], 10);
settings.set(['availability'], true);
settings.set(['devices', devices.bulb_color_2.ieeeAddr, 'availability'], false);
Object.values(devices).forEach(d => d.lastSeen = utils.minutes(1));
mocks.forEach((m) => m.mockClear());
await resetExtension();
Object.values(devices).forEach((d) => d.ping.mockClear());
});
afterEach(async () => {
Object.values(devices).forEach(d => d.lastSeen = utils.minutes(1));
})
afterAll(async () => {
jest.useRealTimers();
})
it('Should publish availabilty on startup', async () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb_color/availability',
'online',
{ retain: true, qos: 0 },
expect.any(Function)
);
it('Should publish availabilty on startup for device where it is enabled for', async () => {
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
'online', {retain: true, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote/availability',
'online', {retain: true, qos: 0}, expect.any(Function));
expect(MQTT.publish).not.toHaveBeenCalledWith('zigbee2mqtt/bulb_color_2/availability',
'online', {retain: true, qos: 0}, expect.any(Function));
});
it('Should publish availabilty offline when ping fails', async () => {
it('Should publish offline for active device when not seen for 10 minutes', async () => {
MQTT.publish.mockClear();
logger.error.mockClear();
logger.debug.mockClear();
const device = zigbeeHerdsman.devices.bulb_color;
device.ping.mockImplementationOnce(() => {throw new Error('failed')});
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenNthCalledWith(1,
'zigbee2mqtt/bulb_color/availability',
'offline',
{ retain: true, qos: 0 },
expect.any(Function)
);
expect(logger.error).toHaveBeenCalledTimes(1);
expect(logger.error).toHaveBeenCalledWith("Failed to ping 'bulb_color'");
device.ping.mockImplementationOnce(() => {throw new Error('failed')});
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
expect(logger.debug).toHaveBeenCalledWith("Failed to ping 'bulb_color'");
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenNthCalledWith(1,
'zigbee2mqtt/bulb_color/availability',
'offline',
{ retain: true, qos: 0 },
expect.any(Function)
);
await advancedTime(utils.minutes(5));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
await advancedTime(utils.minutes(7));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1);
expect(devices.bulb_color.ping).toHaveBeenNthCalledWith(1, true);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
});
it('Should publish availabilty online and query state on reconnect', async () => {
const device = zigbeeHerdsman.devices.E11_G13;
const endpoint = device.getEndpoint(1);
device.ping.mockImplementationOnce(() => {throw new Error('failed')});
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
it('Shouldnt do anything for a device when availability: false is set for device', async () => {
MQTT.publish.mockClear();
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb_enddevice/availability',
'online',
{ retain: true, qos: 0 },
expect.any(Function)
);
await zigbeeHerdsman.events.lastSeenChanged({device: devices.bulb_color_2}); // Coverage satisfaction
expect(endpoint.read).toHaveBeenCalledTimes(2);
expect(endpoint.read).toHaveBeenCalledWith('genLevelCtrl', ['currentLevel']);
expect(endpoint.read).toHaveBeenCalledWith('genOnOff', ['onOff']);
await advancedTime(utils.minutes(12));
expect(devices.bulb_color_2.ping).toHaveBeenCalledTimes(0);
});
it('Should fail gracefully when quering state after reconnect fails', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
it('Should publish offline for passive device when not seen for 25 hours', async () => {
MQTT.publish.mockClear();
await advancedTime(utils.hours(26));
expect(devices.remote.ping).toHaveBeenCalledTimes(0);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
});
it('Should reset ping timer when device last seen changes for active device', async () => {
MQTT.publish.mockClear();
await advancedTime(utils.minutes(5));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
await zigbeeHerdsman.events.lastSeenChanged({device: devices.bulb_color});
await advancedTime(utils.minutes(7));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
await advancedTime(utils.minutes(10));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1);
expect(devices.bulb_color.ping).toHaveBeenNthCalledWith(1, true);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
});
it('Should ping again when first ping fails', async () => {
MQTT.publish.mockClear();
await advancedTime(utils.minutes(5));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
await zigbeeHerdsman.events.lastSeenChanged({device: devices.bulb_color});
await advancedTime(utils.minutes(7));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
devices.bulb_color.ping.mockImplementationOnce(() => {throw new Error('failed')});
devices.bulb_color.lastSeen = Date.now() + utils.minutes(10);
await advancedTime(utils.minutes(10));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(2);
expect(devices.bulb_color.ping).toHaveBeenNthCalledWith(1, true);
expect(devices.bulb_color.ping).toHaveBeenNthCalledWith(2, false);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
});
it('Should reset ping timer when device last seen changes for passive device', async () => {
MQTT.publish.mockClear();
await advancedTime(utils.hours(24));
expect(devices.remote.ping).toHaveBeenCalledTimes(0);
await zigbeeHerdsman.events.lastSeenChanged({device: devices.remote});
await advancedTime(utils.hours(25));
expect(devices.remote.ping).toHaveBeenCalledTimes(0);
devices.remote.ping.mockImplementationOnce(() => {throw new Error('failed')});
await advancedTime(utils.hours(3));
expect(devices.remote.ping).toHaveBeenCalledTimes(0);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
});
it('Should immediately mark device as online when it lastSeen changes', async () => {
MQTT.publish.mockClear();
await advancedTime(utils.minutes(15));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
devices.bulb_color.lastSeen = Date.now();
await zigbeeHerdsman.events.lastSeenChanged({device: devices.bulb_color});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
'online', {retain: true, qos: 0}, expect.any(Function));
});
it('Should allow to change availability timeout via device options', async () => {
settings.set(['devices', '0x000b57fffec6a5b3', 'availability'], {timeout: 40});
await resetExtension();
MQTT.publish.mockClear();
await advancedTime(utils.minutes(25));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
await advancedTime(utils.minutes(17));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1);
});
it('Should not ping disabled devices', async () => {
settings.set(['devices', devices.bulb_color.ieeeAddr, 'disabled'], true);
await resetExtension();
MQTT.publish.mockClear();
await advancedTime(utils.minutes(15));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
});
it('Should allow to change availability timeout via avaiability options', async () => {
settings.set(['availability'], {active: {timeout: 30}});
await resetExtension();
MQTT.publish.mockClear();
await advancedTime(utils.minutes(25));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
await advancedTime(utils.minutes(7));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1);
});
it('Should stop pinging device when it leaves', async () => {
await resetExtension();
MQTT.publish.mockClear();
await advancedTime(utils.minutes(9));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
await zigbeeHerdsman.events.deviceLeave({ieeeAddr: devices.bulb_color.ieeeAddr});
await flushPromises();
await advancedTime(utils.minutes(3));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
});
it('Should stop pinging device when it is removed', async () => {
await resetExtension();
MQTT.publish.mockClear();
await advancedTime(utils.minutes(9));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
MQTT.events.message('zigbee2mqtt/bridge/request/device/remove', stringify({id: "bulb_color"}));
await flushPromises();
await advancedTime(utils.minutes(3));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
});
it('Should allow to be disabled', async () => {
settings.set(['availability'], false);
await resetExtension();
await advancedTime(utils.minutes(12));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
});
it('Should to enable availabilty for just one device', async () => {
settings.set(['availability'], false);
settings.set(['devices', devices.bulb_color.ieeeAddr, 'availability'], true);
await resetExtension();
MQTT.publish.mockClear();
await advancedTime(utils.minutes(11));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1);
});
it('Should retrieve device state when it reconnects', async () => {
MQTT.publish.mockClear();
const device = controller.zigbee.resolveEntity(devices.bulb_color.ieeeAddr);
controller.state.set(device, {state: 'OFF'})
const endpoint = devices.bulb_color.getEndpoint(1);
endpoint.read.mockClear();
endpoint.read.mockImplementationOnce(() => {throw new Error('Device timedout')});
device.ping.mockImplementationOnce(() => {throw new Error('failed')});
logger.debug.mockClear();
jest.advanceTimersByTime(11 * 2000);
await zigbeeHerdsman.events.deviceAnnounce({device: devices.bulb_color});
await flushPromises();
await advancedTime(utils.seconds(1));
await zigbeeHerdsman.events.deviceAnnounce({device: devices.bulb_color});
await flushPromises();
expect(endpoint.read).toHaveBeenCalledTimes(0);
await advancedTime(utils.seconds(2));
expect(endpoint.read).toHaveBeenCalledTimes(1);
expect(endpoint.read).toHaveBeenCalledWith('genOnOff', ['onOff']);
expect(logger.error).toHaveBeenCalledWith(`Failed to read state of 'bulb_color' after reconnect`);
});
it('Shouldnt ping again when still pinging', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
device.ping.mockClear();
device.ping.mockImplementationOnce(async () => {await wait(100000000)});
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
expect(device.ping).toHaveBeenCalledTimes(1);
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
expect(device.ping).toHaveBeenCalledTimes(1);
});
it('Should mark device online when receiving message while offline', async () => {
MQTT.publish.mockClear();
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
device.ping.mockImplementationOnce(() => {throw new Error('failed')});
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
MQTT.publish.mockClear();
const data = {modelID: 'test'}
const payload = {data, cluster: 'genOnOff', device, endpoint, type: 'readResponse', linkquality: 10};
await zigbeeHerdsman.events.message(payload);
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenNthCalledWith(1,
'zigbee2mqtt/bulb_color/availability',
'online',
{ retain: true, qos: 0 },
expect.any(Function)
);
});
it('Should retrieve the state when device is turned on/off within availability timeout', async () => {
MQTT.publish.mockClear();
extension.state = {};
const payload = {device: zigbeeHerdsman.devices.bulb_color};
await zigbeeHerdsman.events.deviceJoined(payload);
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb_color/availability',
'online',
{ retain: true, qos: 0 },
expect.any(Function)
);
});
it('Should retrieve the state when device is turned on/off within availability timeout on deviceAnnounce', async () => {
MQTT.publish.mockClear();
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
endpoint.read.mockClear();
await zigbeeHerdsman.events.deviceAnnounce({device});
await zigbeeHerdsman.events.deviceAnnounce({device: devices.bulb_color});
await flushPromises();
expect(endpoint.read).toHaveBeenCalledTimes(2);
expect(endpoint.read).toHaveBeenCalledWith('genLevelCtrl', ['currentLevel']);
expect(endpoint.read).toHaveBeenCalledWith('genOnOff', ['onOff']);
endpoint.read.mockImplementationOnce(() => {throw new Error('')});
await advancedTime(utils.seconds(3));
expect(endpoint.read).toHaveBeenCalledTimes(1);
});
it('Should not retrieve the state when device is turned on/off within availability timeout on deviceJoined', async () => {
it('Should republish availability when device is renamed', async () => {
MQTT.publish.mockClear();
const device = zigbeeHerdsman.devices.E11_G13;
const endpoint = device.getEndpoint(1);
endpoint.read.mockClear();
await zigbeeHerdsman.events.deviceJoined({device});
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', stringify({from: 'bulb_color', to: 'bulb_new_name'}));
await flushPromises();
expect(endpoint.read).toHaveBeenCalledTimes(0);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
null, {retain: true, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_new_name/availability',
'online', {retain: true, qos: 0}, expect.any(Function));
await advancedTime(utils.hours(12));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_new_name/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
});
it('Should not do anything when message has no device', async () => {
MQTT.publish.mockClear();
const device = zigbeeHerdsman.devices.bulb_color;
const payload = {ieeeAddr: device.ieeeAddr};
await zigbeeHerdsman.events.deviceLeave(payload);
await flushPromises();
expect(MQTT.publish.mock.calls.find((c) => c[0].includes('availability'))).toBeUndefined();
});
it('Should not ping devices on blocklist by friendly name', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
settings.set(['advanced', 'availability_blocklist'], ['bulb_color'])
it('Should publish availabiltiy payload in JSON format', async () => {
settings.set(['advanced', 'legacy_availability_payload'], false);
await resetExtension();
device.ping.mockClear();
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
expect(device.ping).toHaveBeenCalledTimes(0);
MQTT.publish.mockClear();
await advancedTime(utils.hours(26));
expect(devices.remote.ping).toHaveBeenCalledTimes(0);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote/availability',
stringify({state: 'offline'}), {retain: true, qos: 0}, expect.any(Function));
});
it('Should not ping devices on blacklist by friendly name', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
settings.set(['advanced', 'availability_blacklist'], ['bulb_color'])
it('Deprecated - should allow to block via advanced.availability_blocklist', async () => {
settings.set(['advanced', 'availability_blocklist'], [devices.bulb_color.ieeeAddr]);
await resetExtension();
device.ping.mockClear();
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
expect(device.ping).toHaveBeenCalledTimes(0);
await advancedTime(utils.minutes(12));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
});
it('Should not ping devices on blocklist by IEEE address', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
settings.set(['advanced', 'availability_blocklist'], [device.ieeeAddr]);
it('Deprecated - should allow to pass certain devices via availability_passlist', async () => {
settings.set(['advanced', 'availability_passlist'], [devices.bulb_color_2.ieeeAddr]);
settings.changeEntityOptions(devices.bulb_color_2.ieeeAddr, {availability: null});
await resetExtension();
device.ping.mockClear();
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
expect(device.ping).toHaveBeenCalledTimes(0);
await advancedTime(utils.minutes(12));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
expect(devices.bulb_color_2.ping).toHaveBeenCalledTimes(1);
});
it('Should ping forced pingable devices', async () => {
const device = zigbeeHerdsman.devices.E11_G13;
const endpoint = device.getEndpoint(1);
const data = {modelID: 'test'}
const payload = {data, cluster: 'genOnOff', device, endpoint, type: 'readResponse', linkquality: 10};
await zigbeeHerdsman.events.message(payload);
await flushPromises();
device.ping.mockClear();
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
expect(device.ping).toHaveBeenCalledTimes(1);
});
it('Should ping devices on passlist by friendly name if availability_passlist is set', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
settings.set(['advanced', 'availability_passlist'], ['bulb_color']);
it('Deprecated - should allow to enable via availability_timeout', async () => {
settings.set(['availability'], false);
settings.set(['advanced', 'availability_timeout'], 60);
await resetExtension();
device.ping.mockClear();
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
expect(device.ping).toHaveBeenCalledTimes(1);
await advancedTime(utils.minutes(12));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1);
});
it('Should ping devices on whitelist by friendly name if availability_whitelist is set', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
settings.set(['advanced', 'availability_whitelist'], ['bulb_color']);
it('Should publish availabilty for groups', async () => {
settings.set(['devices', devices.bulb_color_2.ieeeAddr, 'availability'], true);
await resetExtension();
device.ping.mockClear();
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
expect(device.ping).toHaveBeenCalledTimes(1);
});
it('Should ping devices on passlist by IEEE address if availability_passlist is set', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
settings.set(['advanced', 'availability_passlist'], [device.ieeeAddr]);
await resetExtension();
device.ping.mockClear();
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
expect(device.ping).toHaveBeenCalledTimes(1);
});
it('Should not ping devices not in passlist if availability_passlist is set', async () => {
const device = zigbeeHerdsman.devices.bulb;
device.ping.mockClear();
extension.state[device.ieeeAddr] = false;
settings.set(['advanced', 'availability_passlist'], ['0x000b57fffec6a5b3'])
await resetExtension();
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
expect(device.ping).toHaveBeenCalledTimes(0);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability',
'online', {retain: true, qos: 0}, expect.any(Function));
MQTT.publish.mockClear();
await zigbeeHerdsman.events.deviceAnnounce({device});
});
it('Should not read when device has no modelID and reconnects', async () => {
const device = zigbeeHerdsman.devices.nomodel;
extension.state[device.ieeeAddr] = true;
const endpoint = device.getEndpoint(1);
await zigbeeHerdsman.events.deviceAnnounce({device});
await flushPromises();
expect(endpoint.read).toHaveBeenCalledTimes(0);
});
it('Should not read when device has is unsupported', async () => {
const device = zigbeeHerdsman.devices.unsupported_router;
extension.state[device.ieeeAddr] = true;
const endpoint = device.getEndpoint(1);
await zigbeeHerdsman.events.deviceAnnounce({device});
await flushPromises();
expect(endpoint.read).toHaveBeenCalledTimes(0);
});
it('Should stop pinging device when removed', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
device.ping.mockClear();
const endpoint = device.getEndpoint(1);
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
expect(device.ping).toHaveBeenCalledTimes(1);
settings.removeDevice(device.ieeeAddr);
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
expect(device.ping).toHaveBeenCalledTimes(1);
});
it('Should publish availability when end device joins', async () => {
delete extension.state[zigbeeHerdsman.devices.WXKG02LM_rev1.ieeeAddr];
const device = zigbeeHerdsman.devices.WXKG02LM_rev1;
const payload = {device};
await advancedTime(utils.minutes(12));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
MQTT.publish.mockClear();
await zigbeeHerdsman.events.deviceJoined(payload);
devices.bulb_color_2.lastSeen = Date.now();
await zigbeeHerdsman.events.lastSeenChanged({device: devices.bulb_color_2});
await flushPromises();
expect(device.ping).toHaveBeenCalledTimes(0);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/button_double_key/availability',
'online',
{ retain: true, qos: 0 },
expect.any(Function)
);
});
it('Should mark non-pingable device as non-available when offline for longer than 24 hours', async () => {
const device = zigbeeHerdsman.devices.remote;
const defaultLastSeen = device.lastSeen;
device.lastSeen = Date.now();
MQTT.publish.mockClear();
jest.advanceTimersByTime(1000 * 60 * 60 * 1); // 1 hours
device.lastSeen = device.lastSeen - (1000 * 60 * 60 * 25);
jest.advanceTimersByTime(1000 * 60 * 60 * 1); // 1 hours
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/remote/availability',
'offline',
{ retain: true, qos: 0 },
expect.any(Function)
);
// Shouldn't do anything more when device is removed
settings.removeDevice(device.ieeeAddr);
jest.advanceTimersByTime(1000 * 60 * 60 * 1); // 1 hours
expect(MQTT.publish).toHaveBeenCalledTimes(2);
device.lastSeen = defaultLastSeen;
});
it('Should republish existing state on MQTT connected', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
MQTT.publish.mockClear();
extension.state[device.ieeeAddr] = false;
await extension.onMQTTConnected();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb_color/availability',
'offline',
{ retain: true, qos: 0 },
expect.any(Function)
);
});
it('Should clear retained availability topic when device is remove', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/remove', 'bulb_color');
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb_color/availability',
null,
{retain: true, qos: 0}, expect.any(Function)
);
});
it('Should clear retained availability topic when device is renamed', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', stringify({"from": "bulb_color", "to": "bulb_color_new_name"}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb_color/availability',
null,
{retain: true, qos: 0}, expect.any(Function)
);
});
it('Should clear retained availability topic when device does not exist', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/not_existing_hahaha/availability', 'offline');
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/not_existing_hahaha/availability', null, {retain: true, qos: 0}, expect.any(Function));
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/0x000b57fffec6a5b3/availability', 'offline');
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/0x000b57fffec6a5b3/availability', null, {retain: true, qos: 0}, expect.any(Function));
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bulb_color/availability', 'offline');
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(0);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability',
'online', {retain: true, qos: 0}, expect.any(Function));
});
});
-290
View File
@@ -1,290 +0,0 @@
import data from './stub/data';
import logger from './stub/logger';
import MQTT from './stub/mqtt';
import zigbeeHerdsman from './stub/zigbeeHerdsman';
import * as utils from '../lib/util/utils';
import * as settings from '../lib/util/settings';
import Controller from '../lib/controller';
import flushPromises from './lib/flushPromises';
import stringify from 'json-stable-stringify-without-jsonify';
const mocks = [MQTT.publish, logger.warn, logger.debug];
const devices = zigbeeHerdsman.devices;
zigbeeHerdsman.returnDevices.push(
...[devices.bulb_color.ieeeAddr, devices.bulb_color_2.ieeeAddr, devices.coordinator.ieeeAddr, devices.remote.ieeeAddr])
describe('Availability', () => {
let controller;
let resetExtension = async () => {
await controller.enableDisableExtension(false, 'Availability');
await controller.enableDisableExtension(true, 'Availability');
}
const advancedTime = async (value) => {
jest.setSystemTime(Date.now() + value);
jest.advanceTimersByTime(value);
await flushPromises();
}
beforeAll(async () => {
jest.spyOn(utils, 'sleep').mockImplementation(async (seconds: number) => {});
jest.useFakeTimers('modern');
settings.reRead();
settings.set(['availability'], true);
settings.set(['experimental', 'availability_new'], true);
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
});
beforeEach(async () => {
jest.useFakeTimers('modern').setSystemTime(utils.minutes(1));
data.writeDefaultConfiguration();
settings.reRead();
settings.set(['availability'], true);
settings.set(['experimental', 'availability_new'], true);
settings.set(['devices', devices.bulb_color_2.ieeeAddr, 'availability'], false);
// @ts-ignore
Object.values(devices).forEach(d => d.lastSeen = utils.minutes(1));
mocks.forEach((m) => m.mockClear());
await resetExtension();
// @ts-ignore
Object.values(devices).forEach((d) => d.ping.mockClear());
});
afterEach(async () => {
// @ts-ignore
Object.values(devices).forEach(d => d.lastSeen = utils.minutes(1));
})
afterAll(async () => {
jest.useRealTimers();
})
it('Should publish availabilty on startup for device where it is enabled for', async () => {
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
'online', {retain: true, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote/availability',
'online', {retain: true, qos: 0}, expect.any(Function));
expect(MQTT.publish).not.toHaveBeenCalledWith('zigbee2mqtt/bulb_color_2/availability',
'online', {retain: true, qos: 0}, expect.any(Function));
});
it('Should publish offline for active device when not seen for 10 minutes', async () => {
MQTT.publish.mockClear();
await advancedTime(utils.minutes(5));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
await advancedTime(utils.minutes(7));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1);
expect(devices.bulb_color.ping).toHaveBeenNthCalledWith(1, true);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
});
it('Shouldnt do anything for a device when availability: false is set for device', async () => {
MQTT.publish.mockClear();
await zigbeeHerdsman.events.lastSeenChanged({device: devices.bulb_color_2}); // Coverage satisfaction
await advancedTime(utils.minutes(12));
expect(devices.bulb_color_2.ping).toHaveBeenCalledTimes(0);
});
it('Should publish offline for passive device when not seen for 25 hours', async () => {
MQTT.publish.mockClear();
await advancedTime(utils.hours(26));
expect(devices.remote.ping).toHaveBeenCalledTimes(0);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
});
it('Should reset ping timer when device last seen changes for active device', async () => {
MQTT.publish.mockClear();
await advancedTime(utils.minutes(5));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
await zigbeeHerdsman.events.lastSeenChanged({device: devices.bulb_color});
await advancedTime(utils.minutes(7));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
await advancedTime(utils.minutes(10));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1);
expect(devices.bulb_color.ping).toHaveBeenNthCalledWith(1, true);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
});
it('Should ping again when first ping fails', async () => {
MQTT.publish.mockClear();
await advancedTime(utils.minutes(5));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
await zigbeeHerdsman.events.lastSeenChanged({device: devices.bulb_color});
await advancedTime(utils.minutes(7));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
devices.bulb_color.ping.mockImplementationOnce(() => {throw new Error('failed')});
devices.bulb_color.lastSeen = Date.now() + utils.minutes(10);
await advancedTime(utils.minutes(10));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(2);
expect(devices.bulb_color.ping).toHaveBeenNthCalledWith(1, true);
expect(devices.bulb_color.ping).toHaveBeenNthCalledWith(2, false);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
});
it('Should reset ping timer when device last seen changes for passive device', async () => {
MQTT.publish.mockClear();
await advancedTime(utils.hours(24));
expect(devices.remote.ping).toHaveBeenCalledTimes(0);
await zigbeeHerdsman.events.lastSeenChanged({device: devices.remote});
await advancedTime(utils.hours(25));
expect(devices.remote.ping).toHaveBeenCalledTimes(0);
devices.remote.ping.mockImplementationOnce(() => {throw new Error('failed')});
await advancedTime(utils.hours(3));
expect(devices.remote.ping).toHaveBeenCalledTimes(0);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
});
it('Should immediately mark device as online when it lastSeen changes', async () => {
MQTT.publish.mockClear();
await advancedTime(utils.minutes(15));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
devices.bulb_color.lastSeen = Date.now();
await zigbeeHerdsman.events.lastSeenChanged({device: devices.bulb_color});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
'online', {retain: true, qos: 0}, expect.any(Function));
});
it('Should allow to change availability timeout via device options', async () => {
settings.set(['devices', '0x000b57fffec6a5b3', 'availability'], {timeout: 40});
await resetExtension();
MQTT.publish.mockClear();
await advancedTime(utils.minutes(25));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
await advancedTime(utils.minutes(17));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1);
});
it('Should allow to change availability timeout via avaiability options', async () => {
settings.set(['availability'], {active: {timeout: 30}});
await resetExtension();
MQTT.publish.mockClear();
await advancedTime(utils.minutes(25));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
await advancedTime(utils.minutes(7));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1);
});
it('Should stop pinging device when it leaves', async () => {
await resetExtension();
MQTT.publish.mockClear();
await advancedTime(utils.minutes(9));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
await zigbeeHerdsman.events.deviceLeave({ieeeAddr: devices.bulb_color.ieeeAddr});
await flushPromises();
await advancedTime(utils.minutes(3));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
});
it('Should allow to be disabled', async () => {
settings.set(['availability'], false);
await resetExtension();
await advancedTime(utils.minutes(12));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
});
it('Should retrieve device state when it reconnects', async () => {
MQTT.publish.mockClear();
const endpoint = devices.bulb_color.getEndpoint(1);
endpoint.read.mockClear();
await zigbeeHerdsman.events.deviceAnnounce({device: devices.bulb_color});
await flushPromises();
await advancedTime(utils.seconds(1));
await zigbeeHerdsman.events.deviceAnnounce({device: devices.bulb_color});
await flushPromises();
expect(endpoint.read).toHaveBeenCalledTimes(0);
await advancedTime(utils.seconds(2));
expect(endpoint.read).toHaveBeenCalledTimes(3);
expect(endpoint.read).toHaveBeenCalledWith('genOnOff', ['onOff']);
expect(endpoint.read).toHaveBeenCalledWith('genLevelCtrl', ['currentLevel']);
expect(endpoint.read).toHaveBeenCalledWith('lightingColorCtrl',
['colorMode', 'currentX', 'currentY', 'enhancedCurrentHue', 'currentSaturation', 'colorTemperature']);
// Should stop when one request fails
endpoint.read.mockClear();
await zigbeeHerdsman.events.deviceAnnounce({device: devices.bulb_color});
await flushPromises();
endpoint.read.mockImplementationOnce(() => {throw new Error('')});
await advancedTime(utils.seconds(3));
expect(endpoint.read).toHaveBeenCalledTimes(1);
});
it('Should republish availability when device is renamed', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', stringify({from: 'bulb_color', to: 'bulb_new_name'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_new_name/availability',
'online', {retain: true, qos: 0}, expect.any(Function));
await advancedTime(utils.hours(12));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_new_name/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
});
it('Deprecated - should allow to block via advanced.availability_blocklist', async () => {
settings.set(['advanced', 'availability_blocklist'], [devices.bulb_color.ieeeAddr]);
await resetExtension();
await advancedTime(utils.minutes(12));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
});
it('Deprecated - should allow to pass certain devices via availability_passlist', async () => {
settings.set(['advanced', 'availability_passlist'], [devices.bulb_color_2.ieeeAddr]);
settings.changeEntityOptions(devices.bulb_color_2.ieeeAddr, {availability: null});
await resetExtension();
await advancedTime(utils.minutes(12));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
expect(devices.bulb_color_2.ping).toHaveBeenCalledTimes(1);
});
it('Deprecated - should allow to enable via availability_timeout', async () => {
settings.set(['availability'], false);
settings.set(['advanced', 'availability_timeout'], 60);
await resetExtension();
await advancedTime(utils.minutes(12));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1);
});
});
+2 -1
View File
@@ -588,6 +588,7 @@ describe('Bind', () => {
it('Should poll bounded Hue bulb when receiving message from scene controller', async () => {
const remote = zigbeeHerdsman.devices.bj_scene_switch;
const data = {"action": "recall_2_row_1"};
zigbeeHerdsman.devices.bulb_color_2.getEndpoint(1).read.mockImplementationOnce(() => {throw new Error('failed')});
const payload = {data, cluster: 'genScenes', device: remote, endpoint: remote.getEndpoint(10), type: 'commandRecall', linkquality: 10, groupID: 0};
await zigbeeHerdsman.events.message(payload);
await flushPromises();
@@ -595,7 +596,7 @@ describe('Bind', () => {
expect(debounce).toHaveBeenCalledTimes(3);
expect(zigbeeHerdsman.devices.bulb_color_2.getEndpoint(1).read).toHaveBeenCalledWith("genOnOff", ["onOff"]);
expect(zigbeeHerdsman.devices.bulb_color_2.getEndpoint(1).read).toHaveBeenCalledWith("genLevelCtrl", ["currentLevel"]);
expect(zigbeeHerdsman.devices.bulb_color_2.getEndpoint(1).read).toHaveBeenCalledWith("lightingColorCtrl", ["currentX", "currentY", "colorTemperature"]);
expect(zigbeeHerdsman.devices.bulb_color_2.getEndpoint(1).read).toHaveBeenCalledWith("lightingColorCtrl", ["currentX", "currentY", "colorTemperature"]);
});
it('Should poll grouped Hue bulb when receiving message from TRADFRI remote', async () => {
+139 -29
View File
File diff suppressed because one or more lines are too long
+19 -18
View File
@@ -65,6 +65,7 @@ describe('Configure', () => {
jest.useFakeTimers();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await jest.runOnlyPendingTimers();
await flushPromises();
});
@@ -72,8 +73,9 @@ describe('Configure', () => {
data.writeDefaultConfiguration();
settings.reRead();
mocksClear.forEach((m) => m.mockClear());
coordinatorEndpoint = zigbeeHerdsman.devices.coordinator.getEndpoint(1);
coordinatorEndpoint = zigbeeHerdsman.devices.coordinator.getEndpoint(1);
await resetExtension();
await jest.runOnlyPendingTimers();
});
afterAll(async () => {
@@ -100,12 +102,23 @@ describe('Configure', () => {
expectBulbConfigured();
});
it('Should reconfigure reporting on reportingDisabled event', async () => {
it('Should not re-configure disabled devices', async () => {
expectBulbConfigured();
const device = zigbeeHerdsman.devices.bulb;
await flushPromises();
mockClear(device);
settings.set(['devices', device.ieeeAddr, 'disabled'], true);
zigbeeHerdsman.events.deviceJoined({device});
await flushPromises();
expectBulbNotConfigured();
controller.eventBus.emit('reportingDisabled', {device})
});
it('Should reconfigure reporting on reconfigure event', async () => {
expectBulbConfigured();
const device = controller.zigbee.resolveEntity(zigbeeHerdsman.devices.bulb);
mockClear(device.zh);
expectBulbNotConfigured();
controller.eventBus.emitReconfigure({device});
await flushPromises();
expectBulbConfigured();
});
@@ -114,7 +127,7 @@ describe('Configure', () => {
expectBulbConfigured();
const device = zigbeeHerdsman.devices.bulb;
mockClear(device);
await zigbeeHerdsman.events.lastSeenChanged({device});
await zigbeeHerdsman.events.deviceInterview({device});
await flushPromises();
expectBulbNotConfigured();
});
@@ -192,19 +205,7 @@ describe('Configure', () => {
expect(logger.warn).toHaveBeenCalledWith(`Skipping configure of 'ZNLDP12LM', device does not require this.`)
});
it('Should not configure when interviewing', async () => {
const device = zigbeeHerdsman.devices.remote;
delete device.meta.configured;
device.interviewing = true;
const endpoint = device.getEndpoint(1);
mockClear(device);
await zigbeeHerdsman.events.lastSeenChanged({device});
await flushPromises();
expectRemoteNotConfigured();
device.interviewing = false;
});
it('Should configure when not interviewCompleted', async () => {
it('Should not configure when interview not completed', async () => {
const device = zigbeeHerdsman.devices.remote;
delete device.meta.configured;
device.interviewCompleted = false;
@@ -212,7 +213,7 @@ describe('Configure', () => {
mockClear(device);
await zigbeeHerdsman.events.lastSeenChanged({device});
await flushPromises();
expectRemoteConfigured();
expectRemoteNotConfigured();
device.interviewCompleted = true;
});
+157 -68
View File
@@ -25,11 +25,13 @@ describe('Controller', () => {
});
beforeEach(() => {
MQTT.restoreOnMock();
zigbeeHerdsman.returnDevices.splice(0);
mockExit = jest.fn();
data.writeDefaultConfiguration();
settings.reRead();
controller = new Controller(jest.fn(), mockExit);
mocksClear.forEach((m) => m.mockClear());
data.writeDefaultConfiguration();
settings.reRead();
data.writeDefaultState();
});
@@ -42,7 +44,6 @@ describe('Controller', () => {
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.start).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.setLED).toHaveBeenCalledTimes(0);
expect(zigbeeHerdsman.setTransmitPower).toHaveBeenCalledTimes(0);
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledWith(true, undefined, undefined);
@@ -119,6 +120,7 @@ describe('Controller', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99}), {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/remote", stringify({"brightness":255}), {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_1", stringify({"state":'ON'}), {"qos": 0, "retain":false}, expect.any(Function));
});
it('Start controller should not publish cached states when disabled', async () => {
@@ -145,7 +147,6 @@ describe('Controller', () => {
logger.error.mockClear();
controller.mqtt.client.reconnecting = true;
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
expect(logger.error).toHaveBeenCalledWith("Not connected to MQTT server!");
controller.mqtt.client.reconnecting = false;
});
@@ -155,7 +156,8 @@ describe('Controller', () => {
await flushPromises();
logger.error.mockClear();
controller.mqtt.client.reconnecting = true;
await controller.publishEntityState('bulb', {state: 'ON', brightness: 50, color_temp: 370, color: {r: 100, g: 50, b: 10}, dummy: {1: 'yes', 2: 'no'}});
const device = controller.zigbee.resolveEntity('bulb');
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!");
@@ -172,14 +174,7 @@ describe('Controller', () => {
it('Should remove device not on passlist on startup', async () => {
settings.set(['passlist'], [zigbeeHerdsman.devices.bulb_color.ieeeAddr]);
await controller.start();
await flushPromises();
expect(zigbeeHerdsman.devices.bulb_color.removeFromNetwork).toHaveBeenCalledTimes(0);
expect(zigbeeHerdsman.devices.bulb.removeFromNetwork).toHaveBeenCalledTimes(1);
});
it('Should remove non whitelisted devices on startup', async () => {
settings.set(['whitelist'], [zigbeeHerdsman.devices.bulb_color.ieeeAddr]);
zigbeeHerdsman.devices.bulb.removeFromNetwork.mockImplementationOnce(() => {throw new Error("dummy")});
await controller.start();
await flushPromises();
expect(zigbeeHerdsman.devices.bulb_color.removeFromNetwork).toHaveBeenCalledTimes(0);
@@ -194,20 +189,24 @@ describe('Controller', () => {
expect(zigbeeHerdsman.devices.bulb.removeFromNetwork).toHaveBeenCalledTimes(0);
});
it('Should remove banned devices on startup', async () => {
settings.set(['ban'], [zigbeeHerdsman.devices.bulb_color.ieeeAddr]);
await controller.start();
await flushPromises();
expect(zigbeeHerdsman.devices.bulb_color.removeFromNetwork).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.devices.bulb.removeFromNetwork).toHaveBeenCalledTimes(0);
});
it('Start controller fails', async () => {
zigbeeHerdsman.start.mockImplementationOnce(() => {throw new Error('failed')});
await controller.start();
expect(mockExit).toHaveBeenCalledTimes(1);
});
it('Start controller fails due to MQTT', async () => {
MQTT.on.mockImplementation((type, handler) => {
if (type === 'error') handler({message: 'addr not found'});
});
await controller.start();
await flushPromises();
expect(logger.error).toHaveBeenCalledWith('MQTT error: addr not found');
expect(logger.error).toHaveBeenCalledWith('MQTT failed to connect, exiting...');
expect(mockExit).toHaveBeenCalledTimes(1);
expect(mockExit).toHaveBeenCalledWith(1, false);
});
it('Start controller with permit join true', async () => {
settings.set(['permit_join'], false);
await controller.start();
@@ -215,13 +214,6 @@ describe('Controller', () => {
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledWith(false, undefined, undefined);
});
it('Start controller with disable_led', async () => {
settings.set(['serial', 'disable_led'], true);
await controller.start();
expect(zigbeeHerdsman.setLED).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.setLED).toHaveBeenCalledWith(false);
});
it('Start controller with transmit power', async () => {
settings.set(['experimental', 'transmit_power'], 14);
await controller.start();
@@ -229,13 +221,13 @@ describe('Controller', () => {
expect(zigbeeHerdsman.setTransmitPower).toHaveBeenCalledWith(14);
});
it('Start controller and stop', async () => {
it('Start controller and stop with restart', async () => {
await controller.start();
await controller.stop();
await controller.stop(true);
expect(MQTT.end).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.stop).toHaveBeenCalledTimes(1);
expect(mockExit).toHaveBeenCalledTimes(1);
expect(mockExit).toHaveBeenCalledWith(0, null);
expect(mockExit).toHaveBeenCalledWith(0, true);
});
it('Start controller and stop', async () => {
@@ -245,7 +237,7 @@ describe('Controller', () => {
expect(MQTT.end).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.stop).toHaveBeenCalledTimes(1);
expect(mockExit).toHaveBeenCalledTimes(1);
expect(mockExit).toHaveBeenCalledWith(1, null);
expect(mockExit).toHaveBeenCalledWith(1, false);
});
it('Start controller adapter disconnects', async () => {
@@ -256,7 +248,7 @@ describe('Controller', () => {
expect(MQTT.end).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.stop).toHaveBeenCalledTimes(1);
expect(mockExit).toHaveBeenCalledTimes(1);
expect(mockExit).toHaveBeenCalledWith(1, null);
expect(mockExit).toHaveBeenCalledWith(1, false);
});
it('Handle mqtt message', async () => {
@@ -301,17 +293,6 @@ describe('Controller', () => {
expect(settings.getDevice(device.ieeeAddr)).not.toBeNull();
});
it('On zigbee event message from unkown device should create it', async () => {
await controller.start();
const device = zigbeeHerdsman.devices.notInSettings;
settings.removeDevice(device.ieeeAddr);
expect(settings.getDevice(device.ieeeAddr)).toBeNull();
const payload = {device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10, groupID: 0, cluster: 'genBasic', data: {modelId: device.modelID}};
await zigbeeHerdsman.events.message(payload);
await flushPromises();
expect(settings.getDevice(device.ieeeAddr)).toStrictEqual({"ID": "0x0017880104e45519", "friendlyName": "0x0017880104e45519", "friendly_name": "0x0017880104e45519"});
});
it('On zigbee deviceJoined', async () => {
await controller.start();
const device = zigbeeHerdsman.devices.bulb;
@@ -452,7 +433,8 @@ describe('Controller', () => {
await controller.start();
settings.set(['experimental', 'output'], 'attribute');
MQTT.publish.mockClear();
await controller.publishEntityState('bulb', {dummy: {1: 'yes', 2: 'no'}, color: {r: 100, g: 50, b: 10}, state: 'ON', test: undefined, test1: null, color_temp: 370, brightness: 50});
const device = controller.zigbee.resolveEntity('bulb');
await controller.publishEntityState(device, {dummy: {1: 'yes', 2: 'no'}, color: {r: 100, g: 50, b: 10}, state: 'ON', test: undefined, test1: null, color_temp: 370, brightness: 50});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/state", "ON", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/brightness", "50", {"qos": 0, "retain": true}, expect.any(Function));
@@ -468,7 +450,8 @@ describe('Controller', () => {
await controller.start();
settings.set(['experimental', 'output'], 'attribute_and_json');
MQTT.publish.mockClear();
await controller.publishEntityState('bulb', {state: 'ON', brightness: 200, color_temp: 370, linkquality: 99});
const device = controller.zigbee.resolveEntity('bulb');
await controller.publishEntityState(device, {state: 'ON', brightness: 200, color_temp: 370, linkquality: 99});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(5);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/state", "ON", {"qos": 0, "retain": true}, expect.any(Function));
@@ -484,7 +467,8 @@ describe('Controller', () => {
settings.set(['experimental', 'output'], 'attribute_and_json');
settings.set(['devices', zigbeeHerdsman.devices.bulb.ieeeAddr, 'filtered_attributes'], ['color_temp', 'linkquality']);
MQTT.publish.mockClear();
await controller.publishEntityState('bulb', {state: 'ON', brightness: 200, color_temp: 370, linkquality: 99});
const device = controller.zigbee.resolveEntity('bulb');
await controller.publishEntityState(device, {state: 'ON', brightness: 200, color_temp: 370, linkquality: 99});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(3);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/state", "ON", {"qos": 0, "retain": true}, expect.any(Function));
@@ -497,7 +481,8 @@ describe('Controller', () => {
settings.set(['experimental', 'output'], 'attribute_and_json');
settings.set(['device_options', 'filtered_attributes'], ['color_temp', 'linkquality']);
MQTT.publish.mockClear();
await controller.publishEntityState('bulb', {state: 'ON', brightness: 200, color_temp: 370, linkquality: 99});
const device = controller.zigbee.resolveEntity('bulb');
await controller.publishEntityState(device, {state: 'ON', brightness: 200, color_temp: 370, linkquality: 99});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(3);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/state", "ON", {"qos": 0, "retain": true}, expect.any(Function));
@@ -505,16 +490,58 @@ describe('Controller', () => {
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({"state":"ON","brightness":200}), {"qos": 0, "retain": true}, expect.any(Function));
});
it('Publish entity state attribute_json output filtered cache', async () => {
await controller.start();
settings.set(['advanced', 'output'], 'attribute_and_json');
settings.set(['devices', zigbeeHerdsman.devices.bulb.ieeeAddr, 'filtered_cache'], ['linkquality']);
MQTT.publish.mockClear();
const device = controller.zigbee.resolveEntity('bulb');
expect(controller.state.state[device.ieeeAddr]).toStrictEqual({"brightness":50,"color_temp":370,"linkquality":99,"state":"ON"});
await controller.publishEntityState(device, {state: 'ON', brightness: 200, color_temp: 370, linkquality: 87});
await flushPromises();
expect(controller.state.state[device.ieeeAddr]).toStrictEqual({"brightness":200,"color_temp":370,"state":"ON"});
expect(MQTT.publish).toHaveBeenCalledTimes(5);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/state", "ON", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/brightness", "200", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/linkquality", "87", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({"state":"ON","brightness":200,"color_temp":370,"linkquality":87}), {"qos": 0, "retain": true}, expect.any(Function));
});
it('Publish entity state attribute_json output filtered cache (device_options)', async () => {
await controller.start();
settings.set(['advanced', 'output'], 'attribute_and_json');
settings.set(['device_options', 'filtered_cache'], ['linkquality']);
MQTT.publish.mockClear();
const device = controller.zigbee.resolveEntity('bulb');
expect(controller.state.state[device.ieeeAddr]).toStrictEqual({"brightness":50,"color_temp":370,"linkquality":99,"state":"ON"});
await controller.publishEntityState(device, {state: 'ON', brightness: 200, color_temp: 370, linkquality: 87});
await flushPromises();
expect(controller.state.state[device.ieeeAddr]).toStrictEqual({"brightness":200,"color_temp":370,"state":"ON"});
expect(MQTT.publish).toHaveBeenCalledTimes(5);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/state", "ON", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/brightness", "200", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/linkquality", "87", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({"state":"ON","brightness":200,"color_temp":370,"linkquality":87}), {"qos": 0, "retain": true}, expect.any(Function));
});
it('Publish entity state with device information', async () => {
await controller.start();
settings.set(['mqtt', 'include_device_information'], true);
MQTT.publish.mockClear();
await controller.publishEntityState('bulb', {state: 'ON'});
let device = controller.zigbee.resolveEntity('bulb');
await controller.publishEntityState(device, {state: 'ON'});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99,"device":{"friendlyName":"bulb","model":"LED1545G12","ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369,"type":"Router","manufacturerID":4476,"powerSource":"Mains (single phase)","dateCode":null, "softwareBuildID": null}}), {"qos": 0, "retain": true}, expect.any(Function));
// Unsupported device should have model "unknown"
await controller.publishEntityState('unsupported2', {state: 'ON'});
device = controller.zigbee.resolveEntity('unsupported2');
await controller.publishEntityState(device, {state: 'ON'});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/unsupported2', stringify({"state":"ON","device":{"friendlyName":"unsupported2","model":"unknown","ieeeAddr":"0x0017880104e45529","networkAddress":6536,"type":"EndDevice","manufacturerID":0,"powerSource":"Battery","dateCode":null, "softwareBuildID": null}}), {"qos": 0, "retain": false}, expect.any(Function));
});
@@ -523,7 +550,8 @@ describe('Controller', () => {
await controller.start();
settings.set(['devices', zigbeeHerdsman.devices.bulb.ieeeAddr, 'retain'], false);
MQTT.publish.mockClear();
await controller.publishEntityState('bulb', {state: 'ON'});
const device = controller.zigbee.resolveEntity('bulb');
await controller.publishEntityState(device, {state: 'ON'});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99}), {"qos": 0, "retain": false}, expect.any(Function));
});
@@ -532,7 +560,8 @@ describe('Controller', () => {
await controller.start();
settings.set(['devices', zigbeeHerdsman.devices.bulb.ieeeAddr, 'retain'], true);
MQTT.publish.mockClear();
await controller.publishEntityState('bulb', {state: 'ON'});
const device = controller.zigbee.resolveEntity('bulb');
await controller.publishEntityState(device, {state: 'ON'});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99}), {"qos": 0, "retain": true}, expect.any(Function));
});
@@ -543,7 +572,8 @@ describe('Controller', () => {
settings.set(['devices', zigbeeHerdsman.devices.bulb.ieeeAddr, 'retain'], true);
settings.set(['devices', zigbeeHerdsman.devices.bulb.ieeeAddr, 'retention'], 37);
MQTT.publish.mockClear();
await controller.publishEntityState('bulb', {state: 'ON'});
const device = controller.zigbee.resolveEntity('bulb');
await controller.publishEntityState(device, {state: 'ON'});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99}), {"qos": 0, "retain": true, "properties": {messageExpiryInterval: 37}}, expect.any(Function));
});
@@ -552,7 +582,8 @@ describe('Controller', () => {
data.writeEmptyState();
await controller.start();
MQTT.publish.mockClear();
await controller.publishEntityState('bulb', {});
const device = controller.zigbee.resolveEntity('bulb');
await controller.publishEntityState(device, {});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(0);
});
@@ -562,8 +593,9 @@ describe('Controller', () => {
data.removeState();
await controller.start();
MQTT.publish.mockClear();
await controller.publishEntityState('bulb', {state: 'ON'});
await controller.publishEntityState('bulb', {brightness: 200});
const device = controller.zigbee.resolveEntity('bulb');
await controller.publishEntityState(device, {state: 'ON'});
await controller.publishEntityState(device, {brightness: 200});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({state: "ON"}), {"qos": 0, "retain": true}, expect.any(Function));
@@ -578,7 +610,7 @@ describe('Controller', () => {
logger.error.mockClear();
controller.state.file = "/";
await controller.state.save();
expect(logger.error).toHaveBeenCalledWith(`Failed to write state to '/' (EISDIR: illegal operation on a directory, open '/')`);
expect(logger.error).toHaveBeenCalledWith(expect.stringMatching(/Failed to write state to \'\/\'/));
});
it('Publish should not cache when set', async () => {
@@ -586,22 +618,15 @@ describe('Controller', () => {
data.writeEmptyState();
await controller.start();
MQTT.publish.mockClear();
await controller.publishEntityState('bulb', {state: 'ON'});
await controller.publishEntityState('bulb', {brightness: 200});
const device = controller.zigbee.resolveEntity('bulb');
await controller.publishEntityState(device, {state: 'ON'});
await controller.publishEntityState(device, {brightness: 200});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({"state":"ON"}), {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({"brightness":200}), {"qos": 0, "retain": true}, expect.any(Function));
});
it('Publish should not do anything for unknown entity', async () => {
await controller.start();
MQTT.publish.mockClear();
await controller.publishEntityState('bulb-unknown', {brightness: 200});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(0);
});
it('Should start when state is corrupted', async () => {
fs.writeFileSync(path.join(data.mockDir, 'state.json'), 'corrupted');
await controller.start();
@@ -620,6 +645,27 @@ describe('Controller', () => {
expect(MQTT.connect).toHaveBeenCalledWith("mqtt://localhost", expected);
});
it('Should republish retained messages on MQTT reconnect', async () => {
await controller.start();
MQTT.publish.mockClear();
MQTT.events['connect']();
await flushPromises();
jest.runOnlyPendingTimers();
expect(MQTT.publish).toHaveBeenCalledTimes(13);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), { retain: true, qos: 0 }, expect.any(Function));
});
it('Should not republish retained messages on MQTT reconnect when retained message are sent', async () => {
await controller.start();
MQTT.publish.mockClear();
MQTT.events['connect']();
await flushPromises();
await MQTT.events.message('zigbee2mqtt/bridge/info', 'dummy');
jest.runOnlyPendingTimers();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/state', expect.any(String), { retain: true, qos: 0 }, expect.any(Function));
});
it('Should prevent any message being published with retain flag when force_disable_retain is set', async () => {
settings.set(['mqtt', 'force_disable_retain'], true);
await controller.mqtt.connect()
@@ -631,11 +677,54 @@ describe('Controller', () => {
});
it('Should disable legacy options on new network start', async () => {
expect(settings.get().advanced.homeassistant_legacy_entity_attributes).toBeTruthy();
settings.set(['homeassistant'], true);
settings.reRead();
expect(settings.get().homeassistant.legacy_entity_attributes).toBeTruthy();
expect(settings.get().advanced.legacy_api).toBeTruthy();
zigbeeHerdsman.start.mockReturnValueOnce('reset');
await controller.start();
expect(settings.get().advanced.homeassistant_legacy_entity_attributes).toBeFalsy();
expect(settings.get().homeassistant.legacy_entity_attributes).toBeFalsy();
expect(settings.get().advanced.legacy_api).toBeFalsy();
});
it('Should publish last seen changes', async () => {
settings.set(['advanced', 'last_seen'], 'epoch');
await controller.start();
await flushPromises();
MQTT.publish.mockClear();
const device = zigbeeHerdsman.devices.remote;
await zigbeeHerdsman.events.lastSeenChanged({device, reason: 'deviceAnnounce'});
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/remote', stringify({"brightness":255,"last_seen":1000}), { qos: 0, retain: true }, expect.any(Function));
});
it('Should not publish last seen changes when reason is messageEmitted', async () => {
settings.set(['advanced', 'last_seen'], 'epoch');
await controller.start();
await flushPromises();
MQTT.publish.mockClear();
const device = zigbeeHerdsman.devices.remote;
await zigbeeHerdsman.events.lastSeenChanged({device, reason: 'messageEmitted'});
expect(MQTT.publish).toHaveBeenCalledTimes(0);
});
it('Ignore messages from coordinator', async () => {
// https://github.com/Koenkk/zigbee2mqtt/issues/9218
await controller.start();
const device = zigbeeHerdsman.devices.coordinator;
const payload = {device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10, cluster: 'genBasic', data: {modelId: device.modelID}};
await zigbeeHerdsman.events.message(payload);
await flushPromises();
expect(logger.debug).toHaveBeenCalledWith(`Received Zigbee message from 'Coordinator', type 'attributeReport', cluster 'genBasic', data '{"modelId":null}' from endpoint 1, ignoring since it is from coordinator`);
});
it('Should remove state of removed device when stopped', async () => {
await controller.start();
const device = controller.zigbee.resolveEntity('bulb');
expect(controller.state.state[device.ieeeAddr]).toStrictEqual({"brightness":50,"color_temp":370,"linkquality":99,"state":"ON"});
device.zh.isDeleted = true;
await controller.stop();
expect(controller.state.state[device.ieeeAddr]).toStrictEqual(undefined);
});
});
+4 -3
View File
@@ -1,5 +1,5 @@
const logger = require('./stub/logger');
const data = require('../lib/util/data');
const data = require('../lib/util/data').default;
const path = require('path');
const tmp = require('tmp');
const fs = require('fs');
@@ -15,12 +15,13 @@ describe('Data', () => {
it('Should return correct path when ZIGBEE2MQTT_DATA set', () => {
const expected = tmp.dirSync().name;
process.env.ZIGBEE2MQTT_DATA = expected;
data.__testingOnly_reload();
data.testingOnlyReload();
const actual = data.getPath();
expect(actual).toBe(expected);
expect(data.joinPath('test')).toStrictEqual(path.join(expected, 'test'));
expect(data.joinPath('/test')).toStrictEqual(path.resolve(expected, '/test'));
delete process.env.ZIGBEE2MQTT_DATA;
data.__testingOnly_reload();
data.testingOnlyReload();
});
});
});
+8 -3
View File
@@ -3,7 +3,7 @@ const logger = require('./stub/logger');
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
const MQTT = require('./stub/mqtt');
const path = require('path');
const rimraf = require('rimraf');
const {rimrafSync} = require('rimraf');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const stringify = require('json-stable-stringify-without-jsonify');
@@ -45,7 +45,7 @@ describe('User extensions', () => {
});
afterEach(() => {
const extensionPath = path.join(data.mockDir, 'extension');
rimraf.sync(extensionPath);
rimrafSync(extensionPath);
})
it('Load user extension', async () => {
@@ -84,7 +84,12 @@ describe('User extensions', () => {
MQTT.events.message('zigbee2mqtt/bridge/request/extension/save', stringify({"name": "foo.js", "code": extensionCode}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/response/extension/save', stringify({"data":{},"error":"Unexpected identifier","status":"error"}), { retain: false, qos: 0 }, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/response/extension/save', expect.any(String), { retain: false, qos: 0 }, expect.any(Function));
const payload = JSON.parse(MQTT.publish.mock.calls[0][1]);
expect(payload).toEqual(
expect.objectContaining({"data":{},"status":"error"})
);
expect(payload.error).toMatch("Unexpected identifier");
});
it('Removes user extension', async () => {
+88 -4
View File
@@ -7,6 +7,7 @@ const Controller = require('../lib/controller');
const stringify = require('json-stable-stringify-without-jsonify');
const flushPromises = require('./lib/flushPromises');
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
const path = require("path");
jest.spyOn(process, 'exit').mockImplementation(() => {});
const mockHTTP = {
@@ -19,6 +20,16 @@ const mockHTTP = {
events: {},
};
const mockHTTPS = {
implementation: {
listen: jest.fn(),
on: (event, handler) => {mockHTTPS.events[event] = handler},
close: jest.fn().mockImplementation((cb) => cb()),
},
variables: {},
events: {},
};
const mockWSocket = {
close: jest.fn(),
};
@@ -48,9 +59,17 @@ jest.mock('http', () => ({
mockHTTP.variables.onRequest = onRequest;
return mockHTTP.implementation;
}),
Agent: jest.fn(),
}));
jest.mock("serve-static", () =>
jest.mock('https', () => ({
createServer: jest.fn().mockImplementation((onRequest) => {
mockHTTPS.variables.onRequest = onRequest;
return mockHTTPS.implementation;
}),
}));
jest.mock("connect-gzip-static", () =>
jest.fn().mockImplementation((path) => {
mockNodeStatic.variables.path = path
return mockNodeStatic.implementation
@@ -98,7 +117,6 @@ describe('Frontend', () => {
await controller.start();
expect(mockNodeStatic.variables.path).toBe("my/dummy/path");
expect(mockHTTP.implementation.listen).toHaveBeenCalledWith(8081, "127.0.0.1");
const mockWSClient = {
implementation: {
terminate: jest.fn(),
@@ -111,6 +129,72 @@ describe('Frontend', () => {
expect(mockWSClient.implementation.terminate).toHaveBeenCalledTimes(1);
expect(mockHTTP.implementation.close).toHaveBeenCalledTimes(1);
expect(mockWS.implementation.close).toHaveBeenCalledTimes(1);
mockWS.implementation.close.mockClear();
mockHTTP.implementation.close.mockClear();
mockHTTP.implementation.listen.mockClear();
mockHTTPS.implementation.listen.mockClear();
});
it('Start/stop unix socket', async () => {
settings.set(['frontend'], {host: "/tmp/zigbee2mqtt.sock"});
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
expect(mockNodeStatic.variables.path).toBe("my/dummy/path");
expect(mockHTTP.implementation.listen).toHaveBeenCalledWith("/tmp/zigbee2mqtt.sock");
const mockWSClient = {
implementation: {
terminate: jest.fn(),
send: jest.fn(),
},
events: {},
};
mockWS.implementation.clients.push(mockWSClient.implementation);
await controller.stop();
expect(mockWSClient.implementation.terminate).toHaveBeenCalledTimes(1);
expect(mockHTTP.implementation.close).toHaveBeenCalledTimes(1);
expect(mockWS.implementation.close).toHaveBeenCalledTimes(1);
mockWS.implementation.close.mockClear();
mockHTTP.implementation.close.mockClear();
mockHTTP.implementation.listen.mockClear();
mockHTTPS.implementation.listen.mockClear();
});
it('Start/stop HTTPS valid', async () => {
settings.set(['frontend','ssl_cert'], path.join(__dirname,'assets','certs','dummy.crt'));
settings.set(['frontend','ssl_key'], path.join(__dirname,'assets','certs','dummy.key'));
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
expect(mockHTTP.implementation.listen).not.toHaveBeenCalledWith(8081, "127.0.0.1");
expect(mockHTTPS.implementation.listen).toHaveBeenCalledWith(8081, "127.0.0.1");
await controller.stop();
mockHTTP.implementation.listen.mockClear();
mockHTTPS.implementation.listen.mockClear();
});
it('Start/stop HTTPS invalid : missing config', async () => {
settings.set(['frontend','ssl_cert'], path.join(__dirname,'assets','certs','dummy.crt'));
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
expect(mockHTTP.implementation.listen).toHaveBeenCalledWith(8081, "127.0.0.1");
expect(mockHTTPS.implementation.listen).not.toHaveBeenCalledWith(8081, "127.0.0.1");
await controller.stop();
mockHTTP.implementation.listen.mockClear();
mockHTTPS.implementation.listen.mockClear();
});
it('Start/stop HTTPS invalid : missing file', async () => {
settings.set(['frontend','ssl_cert'], 'filesNotExists.crt');
settings.set(['frontend','ssl_key'], path.join(__dirname,'assets','certs','dummy.key'));
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
expect(mockHTTP.implementation.listen).toHaveBeenCalledWith(8081, "127.0.0.1");
expect(mockHTTPS.implementation.listen).not.toHaveBeenCalledWith(8081, "127.0.0.1");
await controller.stop();
mockHTTP.implementation.listen.mockClear();
mockHTTPS.implementation.listen.mockClear();
});
it('Websocket interaction', async () => {
@@ -140,7 +224,7 @@ describe('Frontend', () => {
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb_color',
stringify({state: 'ON', linkquality: null}),
stringify({state: 'ON', power_on_behavior:null, linkquality: null, update_available: null, update: {state: null, installed_version: -1, latest_version: -1}}),
{ retain: false, qos: 0 },
expect.any(Function)
);
@@ -151,7 +235,7 @@ describe('Frontend', () => {
// Received message on socket
expect(mockWSClient.implementation.send).toHaveBeenCalledTimes(1);
expect(mockWSClient.implementation.send).toHaveBeenCalledWith(stringify({topic: 'bulb_color', payload: {state: 'ON', linkquality: null}}));
expect(mockWSClient.implementation.send).toHaveBeenCalledWith(stringify({topic: 'bulb_color', payload: {state: 'ON', power_on_behavior:null, linkquality: null, update_available: null, update: {state: null, installed_version: -1, latest_version: -1}}}));
// Shouldnt set when not ready
mockWSClient.implementation.send.mockClear();
+41 -1
View File
@@ -30,7 +30,7 @@ describe('Groups', () => {
await controller.start();
await flushPromises();
});
afterAll(async () => {
jest.useRealTimers();
});
@@ -334,6 +334,22 @@ describe('Groups', () => {
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_1", stringify({"state":"ON"}), {"retain": false, qos: 0}, expect.any(Function));
});
it('Should not publish state change when group changes state and device is disabled', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['devices', device.ieeeAddr, 'disabled'], true);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: [device.ieeeAddr]}});
await resetExtension();
MQTT.publish.mockClear();
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_1", stringify({"state":"ON"}), {"retain": false, qos: 0}, expect.any(Function));
});
it('Should publish state change for group when members state change', async () => {
// Created for https://github.com/Koenkk/zigbee2mqtt/issues/5725
const device = zigbeeHerdsman.devices.bulb_color;
@@ -469,6 +485,30 @@ describe('Groups', () => {
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", stringify({state:"OFF"}), {"retain": false, qos: 0}, expect.any(Function));
});
it('Should publish state change off if any lights within are still on when changed via device when off_state: last_member_state is used', async () => {
const device_1 = zigbeeHerdsman.devices.bulb_color;
const device_2 = zigbeeHerdsman.devices.bulb;
const endpoint_1 = device_1.getEndpoint(1);
const endpoint_2 = device_2.getEndpoint(1);
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint_1);
group.members.push(endpoint_2);
settings.set(['groups'], {
'1': {friendly_name: 'group_1', devices: [device_1.ieeeAddr, device_2.ieeeAddr], retain: false, off_state: 'last_member_state'}
});
await resetExtension();
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
await flushPromises();
MQTT.publish.mockClear();
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(MQTT.publish).toHaveBeenNthCalledWith(1, "zigbee2mqtt/group_1", stringify({state:"OFF"}), {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenNthCalledWith(2, "zigbee2mqtt/bulb_color", stringify({state:"OFF"}), {"retain": false, qos: 0}, expect.any(Function));
});
it('Should not publish state change off if any lights within are still on when changed via shared group', async () => {
const device_1 = zigbeeHerdsman.devices.bulb_color;
const device_2 = zigbeeHerdsman.devices.bulb;
+494 -101
View File
File diff suppressed because it is too large Load Diff
+33 -33
View File
@@ -16,7 +16,7 @@ describe('Bridge legacy', () => {
beforeAll(async () => {
jest.useFakeTimers();
version = await require('../../lib/util/utils').getZigbee2MQTTVersion();
version = await require('../../lib/util/utils').default.getZigbee2MQTTVersion();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
})
@@ -56,7 +56,7 @@ describe('Bridge legacy', () => {
it('Should allow whitelist', async () => {
const bulb_color = zigbeeHerdsman.devices.bulb_color;
const bulb = zigbeeHerdsman.devices.bulb;
expect(settings.get().whitelist).toStrictEqual([]);
expect(settings.get().passlist).toStrictEqual([]);
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/config/whitelist', 'bulb_color');
await flushPromises();
@@ -68,7 +68,7 @@ describe('Bridge legacy', () => {
);
MQTT.publish.mockClear()
expect(settings.get().whitelist).toStrictEqual([bulb_color.ieeeAddr]);
expect(settings.get().passlist).toStrictEqual([bulb_color.ieeeAddr]);
MQTT.events.message('zigbee2mqtt/bridge/config/whitelist', 'bulb');
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
@@ -79,47 +79,47 @@ describe('Bridge legacy', () => {
);
MQTT.publish.mockClear()
expect(settings.get().whitelist).toStrictEqual([bulb_color.ieeeAddr, bulb.ieeeAddr]);
expect(settings.get().passlist).toStrictEqual([bulb_color.ieeeAddr, bulb.ieeeAddr]);
MQTT.events.message('zigbee2mqtt/bridge/config/whitelist', 'bulb');
await flushPromises();
expect(settings.get().whitelist).toStrictEqual([bulb_color.ieeeAddr, bulb.ieeeAddr]);
expect(settings.get().passlist).toStrictEqual([bulb_color.ieeeAddr, bulb.ieeeAddr]);
expect(MQTT.publish).toHaveBeenCalledTimes(0);
});
it('Should allow changing device options', async () => {
const bulb_color = zigbeeHerdsman.devices.bulb_color;
expect(settings.getDevice('bulb_color')).toStrictEqual(
{"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color", "friendly_name": "bulb_color", "retain": false}
{"ID": "0x000b57fffec6a5b3", "friendly_name": "bulb_color", "retain": false}
);
MQTT.events.message('zigbee2mqtt/bridge/config/device_options', stringify({friendly_name: 'bulb_color', options: {retain: true}}));
await flushPromises();
expect(settings.getDevice('bulb_color')).toStrictEqual(
{"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color", "friendly_name": "bulb_color", "retain": true}
{"ID": "0x000b57fffec6a5b3", "friendly_name": "bulb_color", "retain": true}
);
MQTT.events.message('zigbee2mqtt/bridge/config/device_options', stringify({friendly_name: 'bulb_color', optionswrong: {retain: true}}));
await flushPromises();
expect(settings.getDevice('bulb_color')).toStrictEqual(
{"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color", "friendly_name": "bulb_color", "retain": true}
{"ID": "0x000b57fffec6a5b3", "friendly_name": "bulb_color", "retain": true}
);
MQTT.events.message('zigbee2mqtt/bridge/config/device_options', "{friendly_name: 'bulb_color'malformed: {retain: true}}");
await flushPromises();
expect(settings.getDevice('bulb_color')).toStrictEqual(
{"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color", "friendly_name": "bulb_color", "retain": true}
{"ID": "0x000b57fffec6a5b3", "friendly_name": "bulb_color", "retain": true}
);
MQTT.events.message('zigbee2mqtt/bridge/config/device_options', stringify({friendly_name: 'bulb_color', options: {random_setting: true}}));
await flushPromises();
expect(settings.getDevice('bulb_color')).toStrictEqual(
{"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color", "friendly_name": "bulb_color", "random_setting": true, "retain": true}
{"ID": "0x000b57fffec6a5b3", "friendly_name": "bulb_color", "random_setting": true, "retain": true}
);
MQTT.events.message('zigbee2mqtt/bridge/config/device_options', stringify({friendly_name: 'bulb_color', options: {options: {random_1: true}}}));
await flushPromises();
expect(settings.getDevice('bulb_color')).toStrictEqual(
{"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color", "friendly_name": "bulb_color", "random_setting": true, "retain": true, options: {random_1: true}}
{"ID": "0x000b57fffec6a5b3", "friendly_name": "bulb_color", "random_setting": true, "retain": true, options: {random_1: true}}
);
MQTT.events.message('zigbee2mqtt/bridge/config/device_options', stringify({friendly_name: 'bulb_color', options: {options: {random_2: false}}}));
await flushPromises();
expect(settings.getDevice('bulb_color')).toStrictEqual(
{"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color", "friendly_name": "bulb_color", "random_setting": true, "retain": true, options: {random_1: true, random_2: false}}
{"ID": "0x000b57fffec6a5b3", "friendly_name": "bulb_color", "random_setting": true, "retain": true, options: {random_1: true, random_2: false}}
);
});
@@ -197,9 +197,9 @@ describe('Bridge legacy', () => {
});
it('Should allow rename devices', async () => {
const bulb_color2 = {"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color2", "friendly_name": "bulb_color2", "retain": false};
const bulb_color2 = {"ID": "0x000b57fffec6a5b3", "friendly_name": "bulb_color2", "retain": false};
MQTT.publish.mockClear();
expect(settings.getDevice('bulb_color')).toStrictEqual({"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color", "friendly_name": "bulb_color", "retain": false});
expect(settings.getDevice('bulb_color')).toStrictEqual({"ID": "0x000b57fffec6a5b3", "friendly_name": "bulb_color", "retain": false});
MQTT.events.message('zigbee2mqtt/bridge/config/rename', stringify({old: 'bulb_color', new: 'bulb_color2'}));
await flushPromises();
expect(settings.getDevice('bulb_color')).toStrictEqual(null);
@@ -226,10 +226,10 @@ describe('Bridge legacy', () => {
it('Should allow rename groups', async () => {
MQTT.publish.mockClear();
expect(settings.getGroup(1)).toStrictEqual({"ID": 1, devices: [], friendlyName: "group_1", "friendly_name": "group_1", retain: false});
expect(settings.getGroup(1)).toStrictEqual({"ID": 1, devices: [], "friendly_name": "group_1", retain: false});
MQTT.events.message('zigbee2mqtt/bridge/config/rename', stringify({old: 'group_1', new: 'group_1_renamed'}));
await flushPromises();
expect(settings.getGroup(1)).toStrictEqual({"ID": 1, devices: [], friendlyName: "group_1_renamed", "friendly_name": "group_1_renamed", retain: false});
expect(settings.getGroup(1)).toStrictEqual({"ID": 1, devices: [], "friendly_name": "group_1_renamed", retain: false});
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/log',
stringify({type: 'group_renamed', message: {from: 'group_1', to: 'group_1_renamed'}}),
@@ -243,10 +243,10 @@ describe('Bridge legacy', () => {
const payload = {device};
await zigbeeHerdsman.events.deviceJoined(payload);
await flushPromises();
expect(settings.getDevice('0x000b57fffec6a5b2').friendlyName).toStrictEqual('bulb');
expect(settings.getDevice('0x000b57fffec6a5b2').friendly_name).toStrictEqual('bulb');
MQTT.events.message('zigbee2mqtt/bridge/config/rename_last', 'bulb_new_name');
await flushPromises();
expect(settings.getDevice('0x000b57fffec6a5b2').friendlyName).toStrictEqual('bulb_new_name');
expect(settings.getDevice('0x000b57fffec6a5b2').friendly_name).toStrictEqual('bulb_new_name');
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/log',
stringify({type: 'device_renamed', message: {from: 'bulb', to: 'bulb_new_name'}}),
@@ -259,10 +259,10 @@ describe('Bridge legacy', () => {
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
expect(settings.getDevice('0x000b57fffec6a5b2').friendlyName).toStrictEqual('bulb');
expect(settings.getDevice('0x000b57fffec6a5b2').friendly_name).toStrictEqual('bulb');
MQTT.events.message('zigbee2mqtt/bridge/config/rename_last', 'bulb_new_name');
await flushPromises();
expect(settings.getDevice('0x000b57fffec6a5b2').friendlyName).toStrictEqual('bulb');
expect(settings.getDevice('0x000b57fffec6a5b2').friendly_name).toStrictEqual('bulb');
});
it('Should allow to add groups', async () => {
@@ -275,7 +275,7 @@ describe('Bridge legacy', () => {
{qos: 0, retain: false},
expect.any(Function)
);
expect(settings.getGroup('new_group')).toStrictEqual({"ID": 3, "friendlyName": "new_group", "friendly_name": "new_group", devices: []});
expect(settings.getGroup('new_group')).toStrictEqual({"ID": 3, "friendly_name": "new_group", devices: []});
expect(zigbeeHerdsman.createGroup).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.createGroup).toHaveBeenCalledWith(3);
});
@@ -284,7 +284,7 @@ describe('Bridge legacy', () => {
zigbeeHerdsman.createGroup.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/config/add_group', '{"friendly_name": "new_group"}');
await flushPromises();
expect(settings.getGroup('new_group')).toStrictEqual({"ID": 3, "friendlyName": "new_group", "friendly_name": "new_group", devices: []});
expect(settings.getGroup('new_group')).toStrictEqual({"ID": 3, "friendly_name": "new_group", devices: []});
expect(zigbeeHerdsman.createGroup).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.createGroup).toHaveBeenCalledWith(3);
expect(MQTT.publish).toHaveBeenCalledWith(
@@ -299,7 +299,7 @@ describe('Bridge legacy', () => {
zigbeeHerdsman.createGroup.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/config/add_group', '{"friendly_name": "new_group", "id": 42}');
await flushPromises();
expect(settings.getGroup('new_group')).toStrictEqual({"ID": 42, "friendlyName": "new_group", "friendly_name": "new_group", devices: []});
expect(settings.getGroup('new_group')).toStrictEqual({"ID": 42, "friendly_name": "new_group", devices: []});
expect(zigbeeHerdsman.createGroup).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.createGroup).toHaveBeenCalledWith(42);
expect(MQTT.publish).toHaveBeenCalledWith(
@@ -314,7 +314,7 @@ describe('Bridge legacy', () => {
zigbeeHerdsman.createGroup.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/config/add_group', '{"id": 42}');
await flushPromises();
expect(settings.getGroup('group_42')).toStrictEqual({"ID": 42, "friendlyName": "group_42", "friendly_name": "group_42", devices: []});
expect(settings.getGroup('group_42')).toStrictEqual({"ID": 42, "friendly_name": "group_42", devices: []});
expect(zigbeeHerdsman.createGroup).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.createGroup).toHaveBeenCalledWith(42)
});
@@ -366,7 +366,7 @@ describe('Bridge legacy', () => {
controller.state.state = {'0x000b57fffec6a5b3': {brightness: 100}};
const device = zigbeeHerdsman.devices.bulb_color;
device.removeFromNetwork.mockClear();
expect(settings.get().ban.length).toBe(0);
expect(settings.get().blocklist.length).toBe(0);
await flushPromises();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/config/remove', 'bulb_color');
@@ -381,14 +381,14 @@ describe('Bridge legacy', () => {
expect.any(Function)
);
expect(controller.state.state).toStrictEqual({});
expect(settings.get().ban.length).toBe(0);
expect(settings.get().blocklist.length).toBe(0);
});
it('Should allow to force remove device', async () => {
controller.state.state = {'0x000b57fffec6a5b3': {brightness: 100}};
const device = zigbeeHerdsman.devices.bulb_color;
device.removeFromDatabase.mockClear();
expect(settings.get().ban.length).toBe(0);
expect(settings.get().blocklist.length).toBe(0);
await flushPromises();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/config/force_remove', 'bulb_color');
@@ -403,13 +403,13 @@ describe('Bridge legacy', () => {
expect.any(Function)
);
expect(controller.state.state).toStrictEqual({});
expect(settings.get().ban.length).toBe(0);
expect(settings.get().blocklist.length).toBe(0);
});
it('Should allow to ban device', async () => {
it('Should allow to block device', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
device.removeFromNetwork.mockClear();
expect(settings.get().ban.length).toBe(0);
expect(settings.get().blocklist.length).toBe(0);
await flushPromises();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/config/ban', 'bulb_color');
@@ -423,7 +423,7 @@ describe('Bridge legacy', () => {
{qos: 0, retain: false},
expect.any(Function)
);
expect(settings.get().ban).toStrictEqual(['0x000b57fffec6a5b3']);
expect(settings.get().blocklist).toStrictEqual(['0x000b57fffec6a5b3']);
});
it('Shouldnt crash when removing non-existing device', async () => {
@@ -443,7 +443,7 @@ describe('Bridge legacy', () => {
await flushPromises();
expect(device.removeFromNetwork).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(settings.getDevice('bulb_color')).toStrictEqual({"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color", "friendly_name": "bulb_color", "retain": false})
expect(settings.getDevice('bulb_color')).toStrictEqual({"ID": "0x000b57fffec6a5b3", "friendly_name": "bulb_color", "retain": false})
expect(MQTT.publish).toHaveBeenCalledTimes(1);
});
@@ -457,7 +457,7 @@ describe('Bridge legacy', () => {
await flushPromises();
expect(device.removeFromNetwork).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(settings.getDevice('bulb_color')).toStrictEqual({"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color", "friendly_name": "bulb_color", "retain": false})
expect(settings.getDevice('bulb_color')).toStrictEqual({"ID": "0x000b57fffec6a5b3", "friendly_name": "bulb_color", "retain": false})
expect(MQTT.publish).toHaveBeenCalledTimes(1);
});

Some files were not shown because too many files have changed in this diff Show More