Compare commits

...
339 Commits
Author SHA1 Message Date
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
60 changed files with 18079 additions and 23185 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',
+1 -1
View File
@@ -37,7 +37,7 @@ body:
id: z2m_version
attributes:
label: Zigbee2MQTT version
description: Can be found in the frontend -> settings -> about -> 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
+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
+46 -22
View File
@@ -2,27 +2,20 @@ name: CI
on: [push, pull_request]
permissions:
contents: read
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: 16
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
@@ -38,13 +31,13 @@ jobs:
run: echo ${{ secrets.CR_PAT }} | 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
@@ -76,9 +69,40 @@ 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.CR_PAT }}" \
-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: [14, 16, 18]
runs-on: ${{ matrix.os }}
continue-on-error: true
steps:
- uses: actions/checkout@v3
- name: Cache node-gyp
uses: actions/cache@v3
with:
key: ${{ matrix.os }}-${{ matrix.node }}-node-gyp
path: |
/home/runner/.cache/node-gyp
C:\Users\runneradmin\AppData\Local\node-gyp
/Users/runner/Library/Caches/node-gyp
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
registry-url: https://registry.npmjs.org/
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run eslint
- name: Build
run: npm run build
- name: Test
run: npm run test-with-coverage
+34
View File
@@ -0,0 +1,34 @@
name: "Code Scanning - Action"
on:
pull_request:
push:
jobs:
CodeQL-Build:
# CodeQL runs on ubuntu-latest, windows-latest, and macos-latest
runs-on: ubuntu-latest
permissions:
# required for all workflows
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below).
- name: Autobuild
uses: github/codeql-action/autobuild@v2
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
+14
View File
@@ -0,0 +1,14 @@
name: 'Dependency 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@v1
+33
View File
@@ -0,0 +1,33 @@
on:
workflow_dispatch:
name: GHCR cleanup
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Delete untagged images
uses: actions/github-script@v6
with:
github-token: ${{ secrets.CR_PAT }}
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
+7 -1
View File
@@ -4,11 +4,17 @@ on:
- cron: "0 0 * * *"
workflow_dispatch:
permissions:
contents: read
jobs:
stale:
permissions:
issues: write # for actions/stale to close stale issues
pull-requests: write # for actions/stale to close stale PRs
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3
- uses: actions/stale@v5
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'
+12 -7
View File
@@ -4,21 +4,26 @@ 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
- 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: 16
- 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@v4
with:
commit-message: Update dependencies
branch: "update_deps"
+5 -5
View File
@@ -7,22 +7,22 @@ jobs:
update_frontend:
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
- uses: actions/setup-node@v3
with:
node-version: 14
node-version: 16
- run: npm install zigbee2mqtt-frontend@${{ github.event.client_payload.version }} --save-exact
- uses: peter-evans/create-pull-request@v3
- uses: peter-evans/create-pull-request@v4
id: cpr
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 }}
- run: sleep 5 # Otherwise pull request may not exist yet causing automerge to fail
- uses: peter-evans/enable-pull-request-automerge@v1
- uses: peter-evans/enable-pull-request-automerge@v2
if: steps.cpr.outputs.pull-request-operation == 'created'
with:
token: ${{ secrets.CR_PAT }}
+5 -5
View File
@@ -7,22 +7,22 @@ jobs:
update_zh:
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
- uses: actions/setup-node@v3
with:
node-version: 14
node-version: 16
- run: npm install zigbee-herdsman@${{ github.event.client_payload.version }} --save-exact
- uses: peter-evans/create-pull-request@v3
- uses: peter-evans/create-pull-request@v4
id: cpr
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 }}
- run: sleep 5 # Otherwise pull request may not exist yet causing automerge to fail
- uses: peter-evans/enable-pull-request-automerge@v1
- uses: peter-evans/enable-pull-request-automerge@v2
if: steps.cpr.outputs.pull-request-operation == 'created'
with:
token: ${{ secrets.CR_PAT }}
+5 -5
View File
@@ -7,22 +7,22 @@ jobs:
update_zhc:
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
- uses: actions/setup-node@v3
with:
node-version: 14
node-version: 16
- run: npm install zigbee-herdsman-converters@${{ github.event.client_payload.version }} --save-exact
- uses: peter-evans/create-pull-request@v3
- uses: peter-evans/create-pull-request@v4
id: cpr
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 }}
- run: sleep 5 # Otherwise pull request may not exist yet causing automerge to fail
- uses: peter-evans/enable-pull-request-automerge@v1
- uses: peter-evans/enable-pull-request-automerge@v2
if: steps.cpr.outputs.pull-request-operation == 'created'
with:
token: ${{ secrets.CR_PAT }}
+5 -10
View File
@@ -1,20 +1,15 @@
FROM node:14-alpine3.12 as base
FROM node:16-alpine3.15 as base
WORKDIR /app
RUN apk add --no-cache tzdata eudev tini
COPY package.json ./
# 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 && \
npm ci --production --no-audit --no-optional --no-update-notifier && \
apk del .buildtools
@@ -22,8 +17,8 @@ RUN apk add --no-cache --virtual .buildtools make gcc g++ python3 linux-headers
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
@@ -34,4 +29,4 @@ ARG COMMIT
RUN echo "$COMMIT" > dist/.hash
ENTRYPOINT ["docker-entrypoint.sh"]
CMD [ "/sbin/tini", "--", "node", "index.js"]
CMD [ "/sbin/tini", "--", "node", "index.js"]
+16 -8
View File
@@ -2,8 +2,8 @@ require('core-js/features/object/from-entries');
require('core-js/features/array/flat');
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');
@@ -15,12 +15,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);
}
}
@@ -44,7 +44,15 @@ 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) => {
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) {
@@ -101,14 +109,14 @@ 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);
}
}
+11 -9
View File
@@ -45,11 +45,12 @@ class Controller {
private state: State;
private mqtt: MQTT;
private restartCallback: () => void;
private exitCallback: (code: number) => void;
private exitCallback: (code: number, restart: boolean) => void;
private extensions: Extension[];
private extensionArgs: ExtensionArgs;
constructor(restartCallback: () => void, exitCallback: (code: number) => void) {
logger.init();
this.eventBus = new EventBus( /* istanbul ignore next */ (error) => {
logger.error(`Error: ${error.message}`);
logger.debug(error.stack);
@@ -111,6 +112,7 @@ class Controller {
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');
}
@@ -153,9 +155,9 @@ class Controller {
// Send all cached states.
if (settings.get().advanced.cache_state_send_on_startup && settings.get().advanced.cache_state) {
for (const device of devices) {
if (this.state.exists(device)) {
this.publishEntityState(device, this.state.get(device));
for (const entity of [...devices, ...this.zigbee.groups()]) {
if (this.state.exists(entity)) {
this.publishEntityState(entity, this.state.get(entity));
}
}
}
@@ -185,7 +187,7 @@ class Controller {
await this.callExtensions('start', [extension]);
}
async stop(): Promise<void> {
async stop(restart = false): Promise<void> {
// Call extensions
await this.callExtensions('stop', this.extensions);
this.eventBus.removeListeners(this);
@@ -197,16 +199,16 @@ class Controller {
try {
await this.zigbee.stop();
logger.info('Stopped Zigbee2MQTT');
await this.exit(0);
await this.exit(0, restart);
} catch (error) {
logger.error('Failed to stop Zigbee2MQTT');
await this.exit(1);
await this.exit(1, restart);
}
}
async exit(code: number): Promise<void> {
async exit(code: number, restart = false): Promise<void> {
await logger.end();
this.exitCallback(code);
this.exitCallback(code, restart);
}
@bind async onZigbeeAdapterDisconnected(): Promise<void> {
+7
View File
@@ -27,6 +27,13 @@ export default class EventBus {
this.on('permitJoinChanged', callback, key);
}
public emitPublishAvailability(): void {
this.emitter.emit('publishAvailability');
}
public onPublishAvailability(key: ListenerKey, callback: () => void): void {
this.on('publishAvailability', callback, key);
}
public emitEntityRenamed(data: eventdata.EntityRenamed): void {
this.emitter.emit('deviceRenamed', data);
}
+61 -34
View File
@@ -5,6 +5,12 @@ import * as settings from '../util/settings';
import debounce from 'debounce';
import bind from 'bind-decorator';
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} = {};
@@ -18,7 +24,8 @@ export default class Availability extends Extension {
}
const key = this.isActiveDevice(device) ? 'active' : 'passive';
const value = settings.get().availability[key]?.timeout;
let value = settings.get().availability?.[key]?.timeout;
if (value == null) value = key == 'active' ? 10 : 1500;
return utils.minutes(value);
}
@@ -27,9 +34,14 @@ export default class Availability extends Extension {
device.zh.powerSource === 'Mains (single phase)';
}
private isAvailable(device: Device): boolean {
const ago = Date.now() - device.zh.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 {
@@ -92,7 +104,7 @@ export default class Availability extends Extension {
override async start(): Promise<void> {
this.eventBus.onEntityRenamed(this, (data) => {
if (data.entity.isDevice() && utils.isAvailabilityEnabledForDevice(data.entity, settings.get())) {
if (utils.isAvailabilityEnabledForEntity(data.entity, settings.get())) {
this.mqtt.publish(`${data.from}/availability`, null, {retain: true, qos: 0});
this.publishAvailability(data.entity, false, true);
}
@@ -102,53 +114,67 @@ export default class Availability extends Extension {
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.devices(false)) {
if (utils.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.zh.lastSeen;
if (this.isActiveDevice(device)) {
logger.debug(`Active device '${device.name}' was last seen ` +
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 / utils.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: eventdata.LastSeenChanged): void {
if (utils.isAvailabilityEnabledForDevice(data.device, settings.get())) {
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);
@@ -167,17 +193,18 @@ export default class Availability extends Extension {
* device can send multiple times after each other.
*/
if (device.definition && !device.zh.interviewing && !this.retrieveStateDebouncers[device.ieeeAddr]) {
this.retrieveStateDebouncers[device.ieeeAddr] = debounce(() => {
this.retrieveStateDebouncers[device.ieeeAddr] = debounce(async () => {
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)));
converter?.convertGet?.(device.endpoint(), keys[0],
{message: this.state.get(device) || {}, mapped: device.definition})
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);
}
}, utils.seconds(2));
}
+6 -1
View File
@@ -469,7 +469,12 @@ export default class Bind extends Extension {
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);
}
+18 -1
View File
@@ -10,6 +10,9 @@ import {detailedDiff} from 'deep-object-diff';
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/(.*)`);
@@ -38,6 +41,7 @@ export default class Bridge extends Extension {
'group/rename': this.groupRename,
'permit_join': this.permitJoin,
'restart': this.restart,
'backup': this.backup,
'touchlink/factory_reset': this.touchlinkFactoryReset,
'touchlink/identify': this.touchlinkIdentify,
'touchlink/scan': this.touchlinkScan,
@@ -233,6 +237,17 @@ export default class Bridge extends Extension {
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 permitJoin(message: KeyValue | string): Promise<MQTTResponse> {
if (typeof message === 'object' && !message.hasOwnProperty('value')) {
throw new Error('Invalid payload');
@@ -419,7 +434,7 @@ export default class Bridge extends Extension {
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();
@@ -650,6 +665,7 @@ export default class Bridge extends Extension {
network_address: device.zh.networkAddress,
supported: !!device.definition,
friendly_name: device.name,
description: device.options.description,
definition: this.getDefinitionPayload(device),
power_source: device.zh.powerSource,
software_build_id: device.zh.softwareBuildID,
@@ -671,6 +687,7 @@ export default class Bridge extends Extension {
return {
id: g.ID,
friendly_name: g.ID === 901 ? 'default_bind_group' : g.name,
description: g.options.description,
scenes: this.getScenes(g.zh),
members: g.zh.members.map((e) => {
return {ieee_address: e.getDevice().ieeeAddr, endpoint: e.ID};
+1 -1
View File
@@ -85,7 +85,7 @@ export default class Configure extends Extension {
private async configure(device: Device, event: 'started' | 'zigbee_event' | 'reporting_disabled' | 'mqtt_message',
force=false, thowError=false): Promise<void> {
if (!force) {
if (!device.definition?.configure || device.zh.interviewing) {
if (!device.definition?.configure || !device.zh.interviewCompleted) {
return;
}
+1 -5
View File
@@ -100,11 +100,7 @@ export default class Frontend extends Extension {
}
for (const device of this.zigbee.devices(false)) {
let payload: KeyValue = {};
if (this.state.exists(device)) {
payload = {...payload, ...this.state.get(device)};
}
const payload = this.state.get(device);
const lastSeen = settings.get().advanced.last_seen;
/* istanbul ignore if */
if (lastSeen !== 'disable') {
+15 -10
View File
@@ -129,11 +129,10 @@ export default class Groups extends Extension {
if (entity instanceof Device) {
for (const group of groups) {
if (group.zh.hasMember(entity.endpoint(endpointName)) &&
!equals(this.lastOptimisticState[group.ID], payload)) {
if (!payload || payload.state !== 'OFF' || this.areAllMembersOff(group)) {
this.lastOptimisticState[group.ID] = payload;
await this.publishEntityState(group, payload, reason);
}
!equals(this.lastOptimisticState[group.ID], payload) &&
this.shouldPublishPayloadForGroup(group, payload)) {
this.lastOptimisticState[group.ID] = payload;
await this.publishEntityState(group, payload, reason);
}
}
} else {
@@ -161,10 +160,9 @@ export default class Groups extends Extension {
await this.publishEntityState(device, memberPayload, reason);
for (const zigbeeGroup of groups) {
if (zigbeeGroup.zh.hasMember(member)) {
if (!payload || payload.state !== 'OFF' || this.areAllMembersOff(zigbeeGroup)) {
groupsToPublish.add(zigbeeGroup);
}
if (zigbeeGroup.zh.hasMember(member) &&
this.shouldPublishPayloadForGroup(zigbeeGroup, payload)) {
groupsToPublish.add(zigbeeGroup);
}
}
}
@@ -176,12 +174,19 @@ export default class Groups extends Extension {
}
}
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.state === 'ON') {
if (state.state === 'ON') {
return false;
}
}
+94 -41
View File
@@ -95,6 +95,7 @@ export default class HomeAssistant extends Extension {
this.eventBus.onDeviceJoined(this, this.onZigbeeEvent);
this.eventBus.onDeviceInterview(this, this.onZigbeeEvent);
this.eventBus.onDeviceMessage(this, this.onZigbeeEvent);
this.eventBus.onEntityOptionsChanged(this, (data) => this.discover(data.entity, true));
this.mqtt.subscribe(this.statusTopic);
this.mqtt.subscribe(defaultStatusTopic);
@@ -104,6 +105,9 @@ export default class HomeAssistant extends Extension {
for (const entity of [...this.zigbee.devices(false), ...this.zigbee.groups()]) {
this.discover(entity, true);
}
// Send availability messages, this is required if the legacy_availability_payload option has been changed.
this.eventBus.emitPublishAvailability();
}
private exposeToConfig(exposes: zhc.DefinitionExpose[], entityType: 'device' | 'group',
@@ -280,19 +284,11 @@ export default class HomeAssistant extends Extension {
const preset = firstExpose.features.find((f) => f.name === 'preset');
if (preset) {
discoveryEntry.discovery_payload.hold_modes = preset.values;
discoveryEntry.discovery_payload.hold_command_topic = true;
discoveryEntry.discovery_payload.hold_state_template =
discoveryEntry.discovery_payload.preset_modes = preset.values;
discoveryEntry.discovery_payload.preset_mode_command_topic = 'preset';
discoveryEntry.discovery_payload.preset_mode_value_template =
`{{ value_json.${preset.property} }}`;
discoveryEntry.discovery_payload.hold_state_topic = true;
}
const awayMode = firstExpose.features.find((f) => f.name === 'away_mode');
if (awayMode) {
discoveryEntry.discovery_payload.away_mode_command_topic = true;
discoveryEntry.discovery_payload.away_mode_state_topic = true;
discoveryEntry.discovery_payload.away_mode_state_template =
`{{ value_json.${awayMode.property} }}`;
discoveryEntry.discovery_payload.preset_mode_state_topic = true;
}
const tempCalibration = firstExpose.features.find((f) => f.name === 'local_temperature_calibration');
@@ -376,6 +372,8 @@ export default class HomeAssistant extends Extension {
} else if (firstExpose.type === 'cover') {
const position = exposes.find((expose) => expose.features.find((e) => e.name === 'position'));
const tilt = exposes.find((expose) => expose.features.find((e) => e.name === 'tilt'));
const motorState = definitionExposes?.find((e) => e.type === 'enum' && e.name === 'motor_state' &&
e.access === ACCESS_STATE);
const discoveryEntry: DiscoveryEntry = {
type: 'cover',
@@ -394,6 +392,37 @@ export default class HomeAssistant extends Extension {
discoveryEntry.discovery_payload.command_topic = true;
discoveryEntry.discovery_payload.state_topic = !position;
discoveryEntry.discovery_payload.command_topic_prefix = endpoint;
// For curtains that have `motor_state` lookup a possible state names and make this
// available for discovery. If the curtains only support the `running` value,
// then we use it anyway. The movement direction is calculated (assumed) in this case.
if (motorState) {
const openingLookup = ['opening', 'open', 'forward', 'up', 'rising'];
const closingLookup = ['closing', 'close', 'backward', 'back', 'reverse', 'down', 'declining'];
const stoppedLookup = ['stopped', 'stop', 'pause', 'paused'];
const openingState = motorState.values.find((s) => openingLookup.includes(s.toLowerCase()));
const closingState = motorState.values.find((s) => closingLookup.includes(s.toLowerCase()));
const stoppedState = motorState.values.find((s) => stoppedLookup.includes(s.toLowerCase()));
if (openingState && closingState && stoppedState) {
discoveryEntry.discovery_payload.state_topic = true;
discoveryEntry.discovery_payload.state_opening = openingState;
discoveryEntry.discovery_payload.state_closing = closingState;
discoveryEntry.discovery_payload.state_stopped = stoppedState;
discoveryEntry.discovery_payload.value_template = `{% if not value_json.motor_state %} ` +
`${stoppedState} {% else %} {{ value_json.motor_state }} {% endif %}`;
}
} else {
const running = definitionExposes?.find((e) => e.type === 'binary' && e.name === 'running');
if (running) {
discoveryEntry.discovery_payload.state_topic = true;
discoveryEntry.discovery_payload.value_template = `{% if not value_json.running %} ` +
`stopped {% else %} {% if value_json.position > 0 %} closing {% else %} ` +
`opening {% endif %} {% endif %}`;
}
}
}
if (!position && !tilt) {
@@ -477,7 +506,7 @@ export default class HomeAssistant extends Extension {
discoveryEntry.discovery_payload.speed_range_max = speeds.length - 1;
assert(presets.length !== 0);
discoveryEntry.discovery_payload.preset_mode_state_topic = true;
discoveryEntry.discovery_payload.preset_mode_command_topic = true;
discoveryEntry.discovery_payload.preset_mode_command_topic = 'fan_mode';
discoveryEntry.discovery_payload.preset_mode_value_template =
`{{ value_json.${speed.property} if value_json.${speed.property} in [${presetList}]` +
` else 'None' | default('None') }}`;
@@ -489,12 +518,14 @@ export default class HomeAssistant extends Extension {
const lookup: {[s: string]: KeyValue}= {
battery_low: {entity_category: 'diagnostic', device_class: 'battery'},
button_lock: {entity_category: 'config', icon: 'mdi:lock'},
calibration: {entity_category: 'config', icon: 'mdi:progress-wrench'},
carbon_monoxide: {device_class: 'safety'},
card: {entity_category: 'config', icon: 'mdi:clipboard-check'},
child_lock: {entity_category: 'config', icon: 'mdi:account-lock'},
color_sync: {entity_category: 'config', icon: 'mdi:sync-circle'},
consumer_connected: {entity_category: 'diagnostic', device_class: 'connectivity'},
contact: {device_class: 'door'},
garage_door_contact: {device_class: 'garage_door', payload_on: false, payload_off: true},
eco_mode: {entity_category: 'config', icon: 'mdi:leaf'},
expose_pin: {entity_category: 'config', icon: 'mdi:pin'},
gas: {device_class: 'gas'},
@@ -502,6 +533,7 @@ export default class HomeAssistant extends Extension {
led_disabled_night: {entity_category: 'config', icon: 'mdi:led-off'},
led_indication: {entity_category: 'config', icon: 'mdi:led-on'},
legacy: {entity_category: 'config', icon: 'mdi:cog'},
motor_reversal: {entity_category: 'config', icon: 'mdi:arrow-left-right'},
moving: {device_class: 'moving'},
no_position_support: {entity_category: 'config', icon: 'mdi:minus-circle-outline'},
occupancy: {device_class: 'motion'},
@@ -510,6 +542,7 @@ export default class HomeAssistant extends Extension {
smoke: {device_class: 'smoke'},
sos: {device_class: 'safety'},
tamper: {device_class: 'tamper'},
temperature_scale: {entity_category: 'config', icon: 'mdi:temperature-celsius'},
test: {entity_category: 'diagnostic', icon: 'mdi:test-tube'},
vibration: {device_class: 'vibration'},
water_leak: {device_class: 'moisture'},
@@ -557,6 +590,12 @@ export default class HomeAssistant extends Extension {
}
} else if (firstExpose.type === 'numeric') {
const lookup: {[s: string]: KeyValue} = {
ac_frequency: {device_class: 'frequency', enabled_by_default: false, entity_category: 'diagnostic',
state_class: 'measurement'},
alarm_humidity_max: {device_class: 'humidity', icon: 'mdi:water-plus'},
alarm_humidity_min: {device_class: 'humidity', icon: 'mdi:water-minus'},
alarm_temperature_max: {device_class: 'temperature', icon: 'mdi:thermometer-high'},
alarm_temperature_min: {device_class: 'temperature', icon: 'mdi:thermometer-low'},
angle: {icon: 'angle-acute'},
angle_axis: {icon: 'angle-acute'},
aqi: {device_class: 'aqi', state_class: 'measurement'},
@@ -564,9 +603,11 @@ export default class HomeAssistant extends Extension {
away_preset_days: {entity_category: 'config', icon: 'mdi:timer'},
away_preset_temperature: {entity_category: 'config', icon: 'mdi:thermometer'},
battery: {device_class: 'battery', entity_category: 'diagnostic', state_class: 'measurement'},
battery2: {device_class: 'battery', entity_category: 'diagnostic', state_class: 'measurement'},
battery_voltage: {device_class: 'voltage', entity_category: 'diagnostic', state_class: 'measurement'},
boost_time: {entity_category: 'config', icon: 'mdi:timer'},
calibration: {entity_category: 'config'},
calibration: {entity_category: 'config', icon: 'mdi:wrench-clock'},
calibration_time: {entity_category: 'config', icon: 'mdi:wrench-clock'},
co2: {device_class: 'carbon_dioxide', state_class: 'measurement'},
comfort_temperature: {entity_category: 'config', icon: 'mdi:thermometer'},
cpu_temperature: {
@@ -595,6 +636,7 @@ export default class HomeAssistant extends Extension {
device_temperature: {
device_class: 'temperature', entity_category: 'diagnostic', state_class: 'measurement',
},
duration: {entity_category: 'config', icon: 'mdi:timer'},
eco2: {device_class: 'carbon_dioxide', state_class: 'measurement'},
eco_temperature: {entity_category: 'config', icon: 'mdi:thermometer'},
energy: {device_class: 'energy', state_class: 'total_increasing'},
@@ -602,6 +644,10 @@ export default class HomeAssistant extends Extension {
gas_density: {icon: 'mdi:google-circles-communities', state_class: 'measurement'},
hcho: {icon: 'mdi:air-filter', state_class: 'measurement'},
humidity: {device_class: 'humidity', state_class: 'measurement'},
humidity_calibration: {entity_category: 'config', icon: 'mdi:wrench-clock'},
humidity_max: {entity_category: 'config', icon: 'mdi:water-percent'},
humidity_min: {entity_category: 'config', icon: 'mdi:water-percent'},
illuminance_calibration: {entity_category: 'config', icon: 'mdi:wrench-clock'},
illuminance_lux: {device_class: 'illuminance', state_class: 'measurement'},
illuminance: {device_class: 'illuminance', enabled_by_default: false, state_class: 'measurement'},
linkquality: {
@@ -618,11 +664,15 @@ export default class HomeAssistant extends Extension {
occupancy_timeout: {entity_category: 'config', icon: 'mdi:timer'},
pm10: {device_class: 'pm10', state_class: 'measurement'},
pm25: {device_class: 'pm25', state_class: 'measurement'},
people: {state_class: 'measurement', icon: 'mdi:account-multiple'},
position: {icon: 'mdi:valve', state_class: 'measurement'},
power: {device_class: 'power', entity_category: 'diagnostic', state_class: 'measurement'},
power_factor: {device_class: 'power_factor', enabled_by_default: false,
entity_category: 'diagnostic', state_class: 'measurement'},
precision: {entity_category: 'config', icon: 'mdi:decimal-comma-increase'},
pressure: {device_class: 'pressure', state_class: 'measurement'},
presence_timeout: {entity_category: 'config', icon: 'mdi:timer'},
reporting_time: {entity_category: 'config', icon: 'mdi:clock-time-one-outline'},
requested_brightness_level: {
enabled_by_default: false, entity_category: 'diagnostic', icon: 'mdi:brightness-5',
},
@@ -632,6 +682,9 @@ export default class HomeAssistant extends Extension {
smoke_density: {icon: 'mdi:google-circles-communities', state_class: 'measurement'},
soil_moisture: {icon: 'mdi:water-percent', state_class: 'measurement'},
temperature: {device_class: 'temperature', state_class: 'measurement'},
temperature_calibration: {entity_category: 'config', icon: 'mdi:wrench-clock'},
temperature_max: {entity_category: 'config', icon: 'mdi:thermometer-plus'},
temperature_min: {entity_category: 'config', icon: 'mdi:thermometer-minus'},
transition: {entity_category: 'config', icon: 'mdi:transition'},
voc: {device_class: 'volatile_organic_compounds', state_class: 'measurement'},
vibration_timeout: {entity_category: 'config', icon: 'mdi:timer'},
@@ -709,14 +762,20 @@ export default class HomeAssistant extends Extension {
} else if (firstExpose.type === 'enum') {
const lookup: {[s: string]: KeyValue} = {
action: {icon: 'mdi:gesture-double-tap'},
alarm_humidity: {icon: 'mdi:water-percent-alert'},
alarm_temperature: {icon: 'mdi:thermometer-alert'},
backlight_auto_dim: {entity_category: 'config', icon: 'mdi:brightness-auto'},
backlight_mode: {entity_category: 'config', icon: 'mdi:lightbulb'},
color_power_on_behavior: {entity_category: 'config', icon: 'mdi:palette'},
control_mode: {entity_category: 'config', icon: 'mdi:tune'},
device_mode: {entity_category: 'config', icon: 'mdi:tune'},
effect: {enabled_by_default: false, icon: 'mdi:palette'},
force: {enabled_by_default: false, icon: 'mdi:valve'},
keep_time: {entity_category: 'config', icon: 'mdi:av-timer'},
keypad_lockout: {entity_category: 'config', icon: 'mdi:lock'},
load_detection_mode: {entity_category: 'config', icon: 'mdi:tune'},
load_dimmable: {entity_category: 'diagnostic', icon: 'mdi:chart-bell-curve'},
load_type: {entity_category: 'diagnostic', icon: 'mdi:led-on'},
melody: {entity_category: 'config', icon: 'mdi:music-note'},
mode_phase_control: {entity_category: 'config', icon: 'mdi:tune'},
mode: {entity_category: 'config', icon: 'mdi:tune'},
@@ -724,9 +783,12 @@ export default class HomeAssistant extends Extension {
operation_mode: {entity_category: 'config', icon: 'mdi:tune'},
power_on_behavior: {entity_category: 'config', icon: 'mdi:power-settings'},
power_outage_memory: {entity_category: 'config', icon: 'mdi:power-settings'},
power_supply_mode: {entity_category: 'diagnostic', icon: 'mdi:power-settings'},
power_type: {entity_category: 'diagnostic', icon: 'mdi:lightning-bolt-circle'},
sensitivity: {entity_category: 'config', icon: 'mdi:tune'},
sensors_type: {entity_category: 'config', icon: 'mdi:tune'},
sound_volume: {entity_category: 'config', icon: 'mdi:volume-high'},
status: {icon: 'mdi:state-machine'},
switch_type: {entity_category: 'config', icon: 'mdi:tune'},
thermostat_unit: {entity_category: 'config', icon: 'mdi:thermometer'},
volume: {entity_category: 'config', icon: 'mdi: volume-high'},
@@ -863,7 +925,7 @@ export default class HomeAssistant extends Extension {
}
}
@bind onEntityRenamed(data: eventdata.EntityRenamed): void {
@bind async onEntityRenamed(data: eventdata.EntityRenamed): Promise<void> {
logger.debug(`Refreshing Home Assistant discovery topic for '${data.entity.name}'`);
// Clear before rename so Home Assistant uses new friendly_name
@@ -873,6 +935,10 @@ export default class HomeAssistant extends Extension {
const topic = this.getDiscoveryTopic(config, data.entity);
this.mqtt.publish(topic, null, {retain: true, qos: 0}, this.discoveryTopic, false, false);
}
// Make sure Home Assistant deletes the old entity first otherwise another one (_2) is created
// https://github.com/Koenkk/zigbee2mqtt/issues/12610
await utils.sleep(2);
}
this.discover(data.entity, true);
@@ -893,8 +959,9 @@ export default class HomeAssistant extends Extension {
let configs: DiscoveryEntry[] = [];
if (isDevice) {
for (const expose of entity.exposes()) {
configs.push(...this.exposeToConfig([expose], 'device', entity.definition, entity.exposes()));
const exposes = entity.exposes(); // avoid calling it hundred of times/s
for (const expose of exposes) {
configs.push(...this.exposeToConfig([expose], 'device', entity.definition, exposes));
}
for (const mapping of legacyMapping) {
@@ -1076,14 +1143,16 @@ export default class HomeAssistant extends Extension {
// Availability payload
payload.availability = [{topic: `${settings.get().mqtt.base_topic}/bridge/state`}];
const availabilityEnabled =
entity.isDevice() && utils.isAvailabilityEnabledForDevice(entity, settings.get());
/* istanbul ignore next */
if (availabilityEnabled) {
if (utils.isAvailabilityEnabledForEntity(entity, settings.get())) {
payload.availability_mode = 'all';
payload.availability.push({topic: `${baseTopic}/availability`});
}
if (!settings.get().advanced.legacy_availability_payload) {
payload.availability.forEach((a: KeyValue) => a.value_template = '{{ value_json.state }}');
}
const commandTopicPrefix = payload.command_topic_prefix ? `${payload.command_topic_prefix}/` : '';
delete payload.command_topic_prefix;
const commandTopicPostfix = payload.command_topic_postfix ? `/${payload.command_topic_postfix}` : '';
@@ -1110,22 +1179,6 @@ export default class HomeAssistant extends Extension {
payload.mode_command_topic = `${baseTopic}/${commandTopicPrefix}set/system_mode`;
}
if (payload.hold_command_topic) {
payload.hold_command_topic = `${baseTopic}/${commandTopicPrefix}set/preset`;
}
if (payload.hold_state_topic) {
payload.hold_state_topic = stateTopic;
}
if (payload.away_mode_state_topic) {
payload.away_mode_state_topic = stateTopic;
}
if (payload.away_mode_command_topic) {
payload.away_mode_command_topic = `${baseTopic}/${commandTopicPrefix}set/away_mode`;
}
if (payload.current_temperature_topic) {
payload.current_temperature_topic = stateTopic;
}
@@ -1178,7 +1231,8 @@ export default class HomeAssistant extends Extension {
}
if (payload.preset_mode_command_topic) {
payload.preset_mode_command_topic = `${baseTopic}/${commandTopicPrefix}set/fan_mode`;
payload.preset_mode_command_topic = `${baseTopic}/${commandTopicPrefix}set/` +
payload.preset_mode_command_topic;
}
if (payload.action_topic) {
@@ -1280,10 +1334,9 @@ export default class HomeAssistant extends Extension {
data.message.toLowerCase() === 'online') {
const timer = setTimeout(async () => {
// Publish all device states.
this.mqtt.publish('bridge/state', 'online', {retain: true, qos: 0});
for (const device of this.zigbee.devices(false)) {
if (this.state.exists(device)) {
this.publishEntityState(device, this.state.get(device));
for (const entity of [...this.zigbee.devices(false), ...this.zigbee.groups()]) {
if (this.state.exists(entity)) {
this.publishEntityState(entity, this.state.get(entity));
}
}
+1
View File
@@ -244,6 +244,7 @@ export default class NetworkMap extends Extension {
} catch (error) {
failed.get(device).push('lqi');
logger.error(`Failed to execute LQI for '${device.name}'`);
logger.debug(error.stack);
}
if (includeRoutes) {
+3 -2
View File
@@ -40,10 +40,11 @@ export default class OnEvent extends Extension {
}
private async callOnEvent(device: Device, type: string, data: KeyValue): Promise<void> {
zhc.onEvent(type, data, device.zh);
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.zh, device.options);
await device.definition.onEvent(type, data, device.zh, device.options, state);
}
}
}
+7 -6
View File
@@ -65,24 +65,25 @@ export default class OTAUpdate extends Extension {
}
private removeProgressAndRemainingFromState(device: Device): void {
delete this.state.get(device)?.update?.progress;
delete this.state.get(device)?.update?.remaining;
delete this.state.get(device).update?.progress;
delete this.state.get(device).update?.remaining;
}
@bind private async onZigbeeEvent(data: eventdata.DeviceMessage): Promise<void> {
if (settings.get().ota.disable_automatic_update_check) return;
if (data.type !== 'commandQueryNextImageRequest' || !data.device.definition) return;
if (data.type !== 'commandQueryNextImageRequest' || !data.device.definition ||
this.inProgress.has(data.device.ieeeAddr)) return;
logger.debug(`Device '${data.device.name}' requested OTA`);
const automaticOTACheckDisabled = settings.get().ota.disable_automatic_update_check;
let supportsOTA = data.device.definition.hasOwnProperty('ota');
if (supportsOTA) {
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();
let available = false;
+1 -1
View File
@@ -135,7 +135,7 @@ export default class Publish extends Extension {
}
const device = re instanceof Device ? re.zh : null;
const entitySettings = re.options;
const entityState = this.state.get(re) || {};
const entityState = this.state.get(re);
const membersState = re instanceof Group ?
Object.fromEntries(re.zh.members.map((e) => [e.getDevice().ieeeAddr,
this.state.get(this.zigbee.resolveEntity(e.getDevice().ieeeAddr))])) : null;
+5 -1
View File
@@ -5,6 +5,7 @@ import zigbeeHerdsmanConverters from 'zigbee-herdsman-converters';
export default class Device {
public zh: zh.Device;
private _definition: zhc.Definition;
private _definitionModelID: string;
get ieeeAddr(): string {return this.zh.ieeeAddr;}
get ID(): string {return this.zh.ieeeAddr;}
@@ -13,8 +14,11 @@ export default class Device {
return this.zh.type === 'Coordinator' ? 'Coordinator' : this.options?.friendly_name || this.ieeeAddr;
}
get definition(): zhc.Definition {
if (!this._definition && !this.zh.interviewing) {
// 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;
}
+12 -2
View File
@@ -4,13 +4,23 @@ import zigbeeHerdsmanConverters from 'zigbee-herdsman-converters';
export default class Group {
public zh: zh.Group;
private resolveDevice: (ieeeAddr: string) => Device;
get ID(): number {return this.zh.groupID;}
get options(): GroupOptions {return settings.getGroup(this.ID);}
get options(): GroupOptions {return {...settings.getGroup(this.ID)};}
get name(): string {return this.options?.friendly_name || this.ID.toString();}
constructor(group: zh.Group) {
constructor(group: zh.Group, resolveDevice: (ieeeAddr: string) => Device) {
this.zh = group;
this.resolveDevice = resolveDevice;
}
hasMember(device: Device): boolean {
return !!device.zh.endpoints.find((e) => this.zh.members.includes(e));
}
membersDevices(): Device[] {
return this.zh.members.map((e) => this.resolveDevice(e.getDevice().ieeeAddr)).filter((d) => d);
}
membersDefinitions(): zhc.Definition[] {
+10 -3
View File
@@ -22,7 +22,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,
},
@@ -71,6 +71,7 @@ export default class MQTT {
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 () => {
@@ -94,12 +95,18 @@ export default class MQTT {
logger.info('Connected to MQTT server');
this.subscribe(`${settings.get().mqtt.base_topic}/#`);
await this.publish('bridge/state', 'online', {retain: true, qos: 0});
await this.publishStateOnline();
}
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();
}
+1 -1
View File
@@ -68,7 +68,7 @@ class State {
}
get(entity: Group | Device): KeyValue {
return this.state[entity.ID];
return this.state[entity.ID] || {};
}
set(entity: Group | Device, update: KeyValue, reason: string=null): KeyValue {
+6 -1
View File
@@ -117,7 +117,8 @@ declare global {
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) => 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<boolean>;
updateToLatest: (device: zh.Device, logger: Logger,
@@ -238,6 +239,7 @@ declare global {
device_options: KeyValue,
advanced: {
legacy_api: boolean,
legacy_availability_payload: boolean,
log_rotation: boolean,
log_symlink_current: boolean,
log_output: ('console' | 'file' | 'syslog')[],
@@ -284,6 +286,7 @@ declare global {
legacy?: boolean,
filtered_attributes?: string[],
friendly_name: string,
description?: string,
qos?: 0 | 1 | 2,
}
@@ -291,11 +294,13 @@ declare global {
devices?: string[],
ID?: number,
optimistic?: boolean,
off_state?: 'all_members_off' | 'last_member_state'
filtered_optimistic?: string[],
retrieve_state?: boolean,
homeassistant?: KeyValue,
filtered_attributes?: string[],
friendly_name: string,
description?: string,
qos?: 0 | 1 | 2,
}
}
+2 -1
View File
@@ -4,7 +4,8 @@ declare module 'zigbee-herdsman-converters' {
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): Promise<void>;
export function onEvent(type: string, data: KeyValue, device: zh.Device, settings: KeyValue,
state: KeyValue): Promise<void>;
}
declare module 'zigbee-herdsman-converters/lib/ota/tradfri' {
+98 -83
View File
@@ -9,40 +9,12 @@ import assert from 'assert';
const colorizer = winston.format.colorize();
// 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);
}
}
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;
// 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 levelWithCompensatedLength: {[s: string]: string} = {
'info': 'info ',
'error': 'error',
@@ -50,71 +22,113 @@ const levelWithCompensatedLength: {[s: string]: string} = {
'debug': 'debug',
};
const timestampFormat = (): string => moment().format(settings.get().advanced.timestamp_format);
let logger: winston.Logger;
let fileTransport : winston.transport;
let output: string[];
let directory: string;
let logFilename: string;
let transportsToUse: winston.transport[];
// Setup default console logger
const transportsToUse: winston.transport[] = [
new winston.transports.Console({
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 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;
/* 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,
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 prefix = colorizer.colorize(l, `Zigbee2MQTT:${levelWithCompensatedLength[l]}`);
return `${prefix} ${timestamp.split('.')[0]}: ${message}`;
return `${levelWithCompensatedLength[l]} ${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
}
let fileTransport : winston.transport;
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));
}
// Create logger
const logger = winston.createLogger({transports: transportsToUse, levels: winston.config.syslog.levels});
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 {
@@ -206,5 +220,6 @@ async function end(): Promise<void> {
}
export default {
logOutput, warn, warning, error, info, debug, setLevel, getLevel, cleanup, addTransport, end, winston: logger,
init, logOutput, warn, warning, error, info, debug, setLevel, getLevel, cleanup, addTransport, end,
winston: (): winston.Logger => logger,
};
+26
View File
@@ -432,6 +432,13 @@
"description": "Disables the legacy api (false = disable)",
"default": true
},
"legacy_availability_payload": {
"type": "boolean",
"title": "Legacy availability payload",
"requiresRestart": true,
"description": "Payload to be used for device availabilty and bridge/state topics. true = text, false = JSON",
"default": true
},
"log_rotation": {
"type": "boolean",
"title": "Log rotation",
@@ -827,6 +834,17 @@
"type": "string",
"title": "Icon",
"description": "The user-defined device icon for the frontend. It can be a link to an image (not a path to a file) or base64 encoded data URL like: image/svg+xml;base64,PHN2ZyB3aW....R0aD"
},
"homeassistant": {
"type": ["object", "null"],
"title": "Home Assistant",
"properties": {
"name": {
"type": "string",
"title": "Home Assistant name",
"description": "Name of the device in Home Assistant"
}
}
}
},
"required": ["friendly_name"]
@@ -852,6 +870,14 @@
"qos": {
"type": "number"
},
"off_state": {
"type": ["string"],
"enum": ["all_members_off", "last_member_state"],
"title": "Group off state",
"default": "auto",
"requiresRestart": true,
"description": "Control when to publish state OFF for a group. 'all_members_off': only publish state OFF when all group memebers are in state OFF, 'last_member_state': publish state OFF whenever one of its members changes to OFF"
},
"filtered_attributes": {
"type": "array",
"items": {
+12 -3
View File
@@ -72,6 +72,7 @@ const defaults: RecursivePartial<Settings> = {
device_options: {},
advanced: {
legacy_api: true,
legacy_availability_payload: true,
log_rotation: true,
log_symlink_current: false,
log_output: ['console', 'file'],
@@ -138,7 +139,7 @@ function loadSettingsWithDefaults(): void {
}
if (_settingsWithDefaults.availability || _settingsWithDefaults.advanced?.availability_timeout) {
const defaults = {active: {timeout: 10}, passive: {timeout: 1500}};
const defaults = {};
const s = typeof _settingsWithDefaults.availability === 'object' ? _settingsWithDefaults.availability : {};
// @ts-ignore
_settingsWithDefaults.availability = {};
@@ -195,6 +196,7 @@ function write(): void {
// In case the setting is defined in a separte file (e.g. !secret network_key) update it there.
for (const path of [
['mqtt', 'server'],
['mqtt', 'user'],
['mqtt', 'password'],
['advanced', 'network_key'],
@@ -322,11 +324,18 @@ function read(): Settings {
}
};
if (s.mqtt?.user && s.mqtt?.password) {
if (s.mqtt?.user) {
s.mqtt.user = interpetValue(s.mqtt.user);
}
if (s.mqtt?.password) {
s.mqtt.password = interpetValue(s.mqtt.password);
}
if (s.mqtt?.server) {
s.mqtt.server = interpetValue(s.mqtt.server);
}
if (s.advanced?.network_key) {
s.advanced.network_key = interpetValue(s.advanced.network_key);
}
@@ -389,7 +398,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);
+54 -8
View File
@@ -38,7 +38,7 @@ 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',
];
function capitalize(s: string): string {
@@ -210,6 +210,43 @@ function toSnakeCase(value: string | KeyValue): any {
}
}
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) {
@@ -220,7 +257,7 @@ function validateFriendlyName(name: string, throwFirstError=false): string[] {
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('+')) {
@@ -245,9 +282,13 @@ function sanitizeImageParameter(parameter: string): string {
return sanitized;
}
function isAvailabilityEnabledForDevice(device: Device, settings: Settings): boolean {
if (device.options.hasOwnProperty('availability')) {
return !!device.options.availability;
function isAvailabilityEnabledForEntity(entity: Device | Group, settings: Settings): boolean {
if (entity.isGroup()) {
return !entity.membersDevices().map((d) => isAvailabilityEnabledForEntity(d, settings)).includes(false);
}
if (entity.options.hasOwnProperty('availability')) {
return !!entity.options.availability;
}
// availability_timeout = deprecated
@@ -256,11 +297,11 @@ function isAvailabilityEnabledForDevice(device: Device, settings: Settings): boo
const passlist = settings.advanced.availability_passlist.concat(settings.advanced.availability_whitelist);
if (passlist.length > 0) {
return passlist.includes(device.name) || passlist.includes(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(device.name) && !blocklist.includes(device.ieeeAddr);
return !blocklist.includes(entity.name) && !blocklist.includes(entity.ieeeAddr);
}
const entityIDRegex = new RegExp(`^(.+?)(?:/(${endpointNames.join('|')}|\\d+))?$`);
@@ -277,6 +318,10 @@ 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;
@@ -302,5 +347,6 @@ export default {
equalsPartial, getObjectProperty, getResponse, parseJSON, loadModuleFromText, loadModuleFromFile,
getExternalConvertersDefinitions, removeNullPropertiesFromObject, toNetworkAddressHex, toSnakeCase,
parseEntityID, isEndpoint, isZHGroup, hours, minutes, seconds, validateFriendlyName, sleep,
sanitizeImageParameter, isAvailabilityEnabledForDevice, publishLastSeen,
sanitizeImageParameter, isAvailabilityEnabledForEntity, publishLastSeen, availabilityPayload,
getAllFiles,
};
+6 -2
View File
@@ -183,6 +183,10 @@ export default class Zigbee {
return this.herdsman.isStopping();
}
async backup(): Promise<void> {
return this.herdsman.backup();
}
async getNetworkParameters(): Promise<zh.NetworkParameters> {
return this.herdsman.getNetworkParameters();
}
@@ -219,7 +223,7 @@ export default class Zigbee {
}
}
private resolveDevice(ieeeAddr: string): Device {
@bind private resolveDevice(ieeeAddr: string): Device {
if (!this.deviceLookup[ieeeAddr]) {
const device = this.herdsman.getDeviceByIeeeAddr(ieeeAddr);
device && (this.deviceLookup[ieeeAddr] = new Device(device));
@@ -235,7 +239,7 @@ export default class Zigbee {
private resolveGroup(groupID: number): Group {
const group = this.herdsman.getGroupByID(Number(groupID));
if (group && !this.groupLookup[groupID]) {
this.groupLookup[groupID] = new Group(group);
this.groupLookup[groupID] = new Group(group, this.resolveDevice);
}
return this.groupLookup[groupID];
-22741
View File
File diff suppressed because it is too large Load Diff
+17066
View File
File diff suppressed because it is too large Load Diff
+31 -29
View File
@@ -1,6 +1,6 @@
{
"name": "zigbee2mqtt",
"version": "1.23.0",
"version": "1.26.0",
"description": "Zigbee to MQTT bridge using Zigbee-herdsman",
"main": "index.js",
"repository": {
@@ -8,7 +8,7 @@
"url": "git+https://github.com/Koenkk/zigbee2mqtt.git"
},
"engines": {
"node": "^10 || ^12 || ^14 || ^15 || ^16"
"node": "^14 || ^16 || ^18"
},
"keywords": [
"xiaomi",
@@ -35,55 +35,57 @@
},
"homepage": "https://koenkk.github.io/zigbee2mqtt",
"dependencies": {
"ajv": "^8.9.0",
"ajv": "^8.11.0",
"bind-decorator": "^1.0.11",
"connect-gzip-static": "^2.1.1",
"core-js": "^3.20.3",
"connect-gzip-static": "2.1.1",
"core-js": "^3.23.3",
"debounce": "^1.2.1",
"deep-object-diff": "^1.1.7",
"fast-deep-equal": "^3.1.3",
"finalhandler": "^1.1.2",
"finalhandler": "^1.2.0",
"git-last-commit": "^1.0.1",
"humanize-duration": "^3.27.1",
"humanize-duration": "^3.27.2",
"js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"jszip": "^3.10.0",
"mkdir-recursive": "^0.4.0",
"moment": "^2.29.1",
"mqtt": "4.3.4",
"moment": "^2.29.3",
"mqtt": "4.3.7",
"object-assign-deep": "^0.4.0",
"rimraf": "^3.0.2",
"semver": "^7.3.5",
"semver": "^7.3.7",
"source-map-support": "^0.5.21",
"uri-js": "^4.4.1",
"winston": "^3.5.0",
"winston-syslog": "^2.4.4",
"ws": "^8.4.2",
"zigbee-herdsman": "0.14.10",
"zigbee-herdsman-converters": "14.0.406",
"zigbee2mqtt-frontend": "0.6.71"
"winston": "^3.8.0",
"winston-syslog": "^2.5.0",
"winston-transport": "^4.5.0",
"ws": "^8.8.0",
"zigbee-herdsman": "0.14.40",
"zigbee-herdsman-converters": "14.0.559",
"zigbee2mqtt-frontend": "0.6.103"
},
"devDependencies": {
"@babel/core": "^7.16.12",
"@babel/plugin-proposal-decorators": "^7.16.7",
"@babel/preset-env": "^7.16.11",
"@babel/preset-typescript": "^7.16.7",
"@babel/core": "^7.18.5",
"@babel/plugin-proposal-decorators": "^7.18.2",
"@babel/preset-env": "^7.18.2",
"@babel/preset-typescript": "^7.17.12",
"@types/debounce": "^1.2.1",
"@types/finalhandler": "^1.1.1",
"@types/humanize-duration": "^3.27.1",
"@types/jest": "^27.4.0",
"@types/jest": "^28.1.3",
"@types/js-yaml": "^4.0.5",
"@types/object-assign-deep": "^0.4.0",
"@types/rimraf": "^3.0.2",
"@types/ws": "^8.2.2",
"@typescript-eslint/eslint-plugin": "^5.10.1",
"@typescript-eslint/parser": "^5.10.1",
"babel-jest": "^27.4.6",
"eslint": "^8.8.0",
"@types/ws": "^8.5.3",
"@typescript-eslint/eslint-plugin": "^5.29.0",
"@typescript-eslint/parser": "^5.29.0",
"babel-jest": "^28.1.1",
"eslint": "^8.18.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-jest": "^26.0.0",
"jest": "^27.4.7",
"eslint-plugin-jest": "^26.5.3",
"jest": "^28.1.1",
"tmp": "^0.2.1",
"typescript": "^4.5.5"
"typescript": "^4.7.4"
},
"jest": {
"coverageThreshold": {
+1
View File
@@ -6,5 +6,6 @@ const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
for (const line of text.split('\n')) {
const model = zigbeeHerdsmanConverters.devices.find((d) => d.model === line);
if (!model) throw new Error(`${line} does not exist`);
console.log(`- \`${line}\` ${model.vendor} ${model.description}`);
}
+43 -5
View File
@@ -227,9 +227,23 @@ describe('Availability', () => {
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();
@@ -242,18 +256,15 @@ describe('Availability', () => {
expect(endpoint.read).toHaveBeenCalledTimes(0);
await advancedTime(utils.seconds(2));
expect(endpoint.read).toHaveBeenCalledTimes(3);
expect(endpoint.read).toHaveBeenCalledTimes(1);
expect(endpoint.read).toHaveBeenCalledWith('genOnOff', ['onOff']);
expect(endpoint.read).toHaveBeenCalledWith('genLevelCtrl', ['currentLevel']);
expect(endpoint.read).toHaveBeenCalledWith('lightingColorCtrl',
['colorMode', 'currentX', 'currentY', 'enhancedCurrentHue', 'currentSaturation', 'colorTemperature']);
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(3);
expect(endpoint.read).toHaveBeenCalledTimes(1);
});
it('Should republish availability when device is renamed', async () => {
@@ -271,6 +282,16 @@ describe('Availability', () => {
'offline', {retain: true, qos: 0}, expect.any(Function));
});
it('Should publish availabiltiy payload in JSON format', async () => {
settings.set(['advanced', 'legacy_availability_payload'], false);
await resetExtension();
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('Deprecated - should allow to block via advanced.availability_blocklist', async () => {
settings.set(['advanced', 'availability_blocklist'], [devices.bulb_color.ieeeAddr]);
await resetExtension();
@@ -297,4 +318,21 @@ describe('Availability', () => {
await advancedTime(utils.minutes(12));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1);
});
it('Should publish availabilty for groups', async () => {
settings.set(['devices', devices.bulb_color_2.ieeeAddr, 'availability'], true);
await resetExtension();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability',
'online', {retain: true, qos: 0}, expect.any(Function));
MQTT.publish.mockClear();
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();
devices.bulb_color_2.lastSeen = Date.now();
await zigbeeHerdsman.events.lastSeenChanged({device: devices.bulb_color_2});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability',
'online', {retain: true, qos: 0}, expect.any(Function));
});
});
+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 () => {
+50 -11
View File
File diff suppressed because one or more lines are too long
+2 -14
View File
@@ -192,19 +192,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 +200,7 @@ describe('Configure', () => {
mockClear(device);
await zigbeeHerdsman.events.lastSeenChanged({device});
await flushPromises();
expectRemoteConfigured();
expectRemoteNotConfigured();
device.interviewCompleted = true;
});
+8 -7
View File
@@ -120,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 () => {
@@ -203,7 +204,7 @@ describe('Controller', () => {
await flushPromises();
expect(logger.error).toHaveBeenCalledWith('MQTT failed to connect: addr not found');
expect(mockExit).toHaveBeenCalledTimes(1);
expect(mockExit).toHaveBeenCalledWith(1);
expect(mockExit).toHaveBeenCalledWith(1, false);
});
it('Start controller with permit join true', async () => {
@@ -220,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);
expect(mockExit).toHaveBeenCalledWith(0, true);
});
it('Start controller and stop', async () => {
@@ -236,7 +237,7 @@ describe('Controller', () => {
expect(MQTT.end).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.stop).toHaveBeenCalledTimes(1);
expect(mockExit).toHaveBeenCalledTimes(1);
expect(mockExit).toHaveBeenCalledWith(1);
expect(mockExit).toHaveBeenCalledWith(1, false);
});
it('Start controller adapter disconnects', async () => {
@@ -247,7 +248,7 @@ describe('Controller', () => {
expect(MQTT.end).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.stop).toHaveBeenCalledTimes(1);
expect(mockExit).toHaveBeenCalledTimes(1);
expect(mockExit).toHaveBeenCalledWith(1);
expect(mockExit).toHaveBeenCalledWith(1, false);
});
it('Handle mqtt message', async () => {
@@ -569,7 +570,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 () => {
+24
View File
@@ -469,6 +469,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;
+101 -32
View File
@@ -58,7 +58,12 @@ describe('HomeAssistant extension', () => {
configs.forEach((c) => {
const id = c['type'] + '/' + c['object_id'];
if (cfg_type_object_ids.includes(id)) {
duplicated.push(d.model);
if (typeof d.exposes == 'function') {
// A dynamic function must exposes all possible attributes for the docs
console.warn(`${d.model} dynamic exposes contains duplicated ${id}`)
} else {
duplicated.push(d.model);
}
} else {
cfg_type_object_ids.push(id);
}
@@ -740,32 +745,35 @@ describe('HomeAssistant extension', () => {
let payload;
payload = {
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
"away_mode_command_topic":"zigbee2mqtt/TS0601_thermostat/set/away_mode",
"away_mode_state_template":"{{ value_json.away_mode }}",
"away_mode_state_topic":"zigbee2mqtt/TS0601_thermostat",
"action_template":"{% set values = {None:None,'idle':'off','heat':'heating','cool':'cooling','fan_only':'fan'} %}{{ values[value_json.running_state] }}",
"action_topic":"zigbee2mqtt/TS0601_thermostat",
"availability":[
{
"topic":"zigbee2mqtt/bridge/state"
}
],
"current_temperature_template":"{{ value_json.local_temperature }}",
"current_temperature_topic":"zigbee2mqtt/TS0601_thermostat",
"device":{
"identifiers":[
"zigbee2mqtt_0x0017882104a44559"
],
"manufacturer":"TuYa",
"model":"Radiator valve with thermostat (TS0601_thermostat)",
"name":"TS0601_thermostat",
"sw_version": null,
"identifiers":[
"zigbee2mqtt_0x0017882104a44559"
],
"manufacturer":"TuYa",
"model":"Radiator valve with thermostat (TS0601_thermostat)",
"name":"TS0601_thermostat",
"sw_version":null
},
"hold_command_topic":"zigbee2mqtt/TS0601_thermostat/set/preset",
"hold_modes":[
"schedule",
"manual",
"boost",
"complex",
"comfort",
"eco"
"preset_mode_command_topic":"zigbee2mqtt/TS0601_thermostat/set/preset",
"preset_modes":[
"schedule",
"manual",
"boost",
"complex",
"comfort",
"eco"
],
"hold_state_template":"{{ value_json.preset }}",
"hold_state_topic":"zigbee2mqtt/TS0601_thermostat",
"preset_mode_value_template":"{{ value_json.preset }}",
"preset_mode_state_topic":"zigbee2mqtt/TS0601_thermostat",
"json_attributes_topic":"zigbee2mqtt/TS0601_thermostat",
"max_temp":"35",
"min_temp":"5",
@@ -773,7 +781,9 @@ describe('HomeAssistant extension', () => {
"mode_state_template":"{{ value_json.system_mode }}",
"mode_state_topic":"zigbee2mqtt/TS0601_thermostat",
"modes":[
"heat", "auto", "off"
"heat",
"auto",
"off"
],
"name":"TS0601_thermostat",
"temp_step":0.5,
@@ -993,6 +1003,19 @@ describe('HomeAssistant extension', () => {
await flushPromises();
});
it('Should discover when options change', async () => {
const device = controller.zigbee.resolveEntity(zigbeeHerdsman.devices.bulb);
MQTT.publish.mockClear();
controller.eventBus.emitEntityOptionsChanged({entity: device, from: {}, to: {'test': 123}});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
`homeassistant/light/${device.ID}/light/config`,
expect.any(String),
expect.any(Object),
expect.any(Function),
);
});
it('Should send all status when home assistant comes online (default topic)', async () => {
data.writeDefaultState();
extension.state.load();
@@ -1016,6 +1039,12 @@ describe('HomeAssistant extension', () => {
{ retain: true, qos: 0 },
expect.any(Function)
);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/group_1',
stringify({"state":'ON'}),
{ retain: false, qos: 0 },
expect.any(Function)
);
});
it('Should send all status when home assistant comes online', async () => {
@@ -1152,6 +1181,8 @@ describe('HomeAssistant extension', () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', stringify({"from": "weather_sensor", "to": "weather_sensor_renamed","homeassistant_rename":true}));
await flushPromises();
jest.runOnlyPendingTimers();
await flushPromises();
const payload = {
'unit_of_measurement': '°C',
@@ -1214,6 +1245,8 @@ describe('HomeAssistant extension', () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/rename', stringify({"from": "ha_discovery_group", "to": "ha_discovery_group_new","homeassistant_rename":true}));
await flushPromises();
jest.runOnlyPendingTimers();
await flushPromises();
const payload = {
"availability":[{"topic":"zigbee2mqtt/bridge/state"}],
@@ -1328,7 +1361,7 @@ describe('HomeAssistant extension', () => {
it('Should discover trigger when click is published', async () => {
const discovered = MQTT.publish.mock.calls.filter((c) => c[0].includes('0x0017880104e45520')).map((c) => c[0]);
expect(discovered.length).toBe(5);
expect(discovered.length).toBe(7);
expect(discovered).toContain('homeassistant/sensor/0x0017880104e45520/click/config');
expect(discovered).toContain('homeassistant/sensor/0x0017880104e45520/action/config');
@@ -1403,21 +1436,21 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/button',
stringify({action: "single", click: "single", battery: null, linkquality: null, voltage: null}),
stringify({action: "single", click: "single", battery: null, linkquality: null, voltage: null, power_outage_count: null, device_temperature: null}),
{ retain: false, qos: 0 },
expect.any(Function),
);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/button',
stringify({action: "", battery: null, linkquality: null, voltage: null, click: null}),
stringify({action: "", battery: null, linkquality: null, voltage: null, click: null, power_outage_count: null, device_temperature: null}),
{ retain: false, qos: 0 },
expect.any(Function),
);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/button',
stringify({click: "", action: null, battery: null, linkquality: null, voltage: null}),
stringify({click: "", action: null, battery: null, linkquality: null, voltage: null, power_outage_count: null, device_temperature: null}),
{ retain: false, qos: 0 },
expect.any(Function),
);
@@ -1504,7 +1537,7 @@ describe('HomeAssistant extension', () => {
await resetExtension();
const discovered = MQTT.publish.mock.calls.filter((c) => c[0].includes('0x0017880104e45520')).map((c) => c[0]);
expect(discovered.length).toBe(4);
expect(discovered.length).toBe(6);
expect(discovered).toContain('homeassistant/sensor/0x0017880104e45520/action/config');
expect(discovered).toContain('homeassistant/sensor/0x0017880104e45520/battery/config');
expect(discovered).toContain('homeassistant/sensor/0x0017880104e45520/linkquality/config');
@@ -1515,7 +1548,7 @@ describe('HomeAssistant extension', () => {
await resetExtension();
const discovered = MQTT.publish.mock.calls.filter((c) => c[0].includes('0x0017880104e45520')).map((c) => c[0]);
expect(discovered.length).toBe(3);
expect(discovered.length).toBe(5);
expect(discovered).not.toContain('homeassistant/sensor/0x0017880104e45520/click/config');
expect(discovered).not.toContain('homeassistant/sensor/0x0017880104e45520/action/config');
@@ -1560,7 +1593,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/button',
stringify({action: "single", "battery":null,"linkquality":null,"voltage":null}),
stringify({action: "single", "battery":null,"linkquality":null,"voltage":null, "power_outage_count": null, "device_temperature": null}),
{ retain: false, qos: 0 },
expect.any(Function),
);
@@ -1597,10 +1630,10 @@ describe('HomeAssistant extension', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(4);
expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/button');
expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({action: 'single', click: null, battery: null, linkquality: null, voltage: null});
expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({action: 'single', click: null, battery: null, linkquality: null, voltage: null, power_outage_count: null, device_temperature: null});
expect(MQTT.publish.mock.calls[0][2]).toStrictEqual({"qos": 0, "retain": false});
expect(MQTT.publish.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/button');
expect(JSON.parse(MQTT.publish.mock.calls[1][1])).toStrictEqual({action: '', click: null, battery: null, linkquality: null, voltage: null});
expect(JSON.parse(MQTT.publish.mock.calls[1][1])).toStrictEqual({action: '', click: null, battery: null, linkquality: null, voltage: null, power_outage_count: null, device_temperature: null});
expect(MQTT.publish.mock.calls[1][2]).toStrictEqual({"qos": 0, "retain": false});
expect(MQTT.publish.mock.calls[2][0]).toStrictEqual('homeassistant/device_automation/0x0017880104e45520/action_single/config');
expect(MQTT.publish.mock.calls[3][0]).toStrictEqual('zigbee2mqtt/button/action');
@@ -1763,6 +1796,42 @@ describe('HomeAssistant extension', () => {
);
});
it('Should discover with json availability payload value_template', async () => {
settings.set(['advanced', 'legacy_availability_payload'], false);
await resetExtension();
const payload = {
"availability":[{"topic":"zigbee2mqtt/bridge/state","value_template":'{{ value_json.state }}'}],
"brightness":true,
"brightness_scale":254,
"color_mode":true,
"command_topic":"zigbee2mqtt/ha_discovery_group/set",
"device":{
"identifiers":["zigbee2mqtt_1221051039810110150109113116116_9"],
"name":"ha_discovery_group",
"sw_version": version,
},
"max_mireds": 454,
"min_mireds": 250,
"json_attributes_topic":"zigbee2mqtt/ha_discovery_group",
"name":"ha_discovery_group",
"schema":"json",
"state_topic":"zigbee2mqtt/ha_discovery_group",
"supported_color_modes":[
"xy",
"color_temp"
],
"unique_id":"9_light_zigbee2mqtt"
};
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/light/1221051039810110150109113116116_9/light/config',
stringify(payload),
{ retain: true, qos: 0 },
expect.any(Function),
);
});
it('Should discover last_seen when enabled', async () => {
settings.set(['advanced', 'last_seen'], 'ISO_8601');
await resetExtension();
+26 -13
View File
@@ -25,6 +25,7 @@ describe('Logger', () => {
it('Create log directory', () => {
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
const dirs = fs.readdirSync(dir.name);
expect(dirs.length).toBe(1);
@@ -32,6 +33,7 @@ describe('Logger', () => {
it('Should cleanup', () => {
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
for (const d of fs.readdirSync(dir.name)) {
@@ -49,6 +51,7 @@ describe('Logger', () => {
it('Should not cleanup when there is no timestamp set', () => {
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
for (let i = 30; i < 40; i++) {
fs.mkdirSync(path.join(dir.name, `log_${i}`));
@@ -62,6 +65,7 @@ describe('Logger', () => {
it('Set and get log level', () => {
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
logger.setLevel('debug');
expect(logger.getLevel()).toBe('debug');
@@ -74,23 +78,26 @@ describe('Logger', () => {
}
const logger = require('../lib/util/logger').default;
expect(logger.winston.transports.length).toBe(2);
logger.init();
expect(logger.winston().transports.length).toBe(2);
logger.addTransport(new DummyTransport());
expect(logger.winston.transports.length).toBe(3);
expect(logger.winston().transports.length).toBe(3);
});
it('Set and get log level warn <-> warning', () => {
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
logger.setLevel('warn');
expect(logger.winston.transports[0].level).toBe('warning');
expect(logger.winston().transports[0].level).toBe('warning');
expect(logger.getLevel()).toBe('warn');
});
it('Logger should be console and file by default', () => {
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
const pipes = logger.winston._readableState.pipes;
const pipes = logger.winston()._readableState.pipes;
expect(pipes.length).toBe(2);
expect(pipes[0].constructor.name).toBe('Console');
expect(pipes[0].silent).toBe(false);
@@ -101,8 +108,9 @@ describe('Logger', () => {
it('Logger can be file only', () => {
settings.set(['advanced', 'log_output'], ['file']);
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
const pipes = logger.winston._readableState.pipes;
const pipes = logger.winston()._readableState.pipes;
expect(pipes.length).toBe(2);
expect(pipes[0].constructor.name).toBe('Console');
expect(pipes[0].silent).toBe(true);
@@ -113,8 +121,9 @@ describe('Logger', () => {
it('Logger can be console only', () => {
settings.set(['advanced', 'log_output'], ['console']);
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
const pipes = logger.winston._readableState.pipes;
const pipes = logger.winston()._readableState.pipes;
expect(pipes.constructor.name).toBe('Console');
expect(pipes.silent).toBe(false);
});
@@ -122,8 +131,9 @@ describe('Logger', () => {
it('Logger can be nothing', () => {
settings.set(['advanced', 'log_output'], []);
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
const pipes = logger.winston._readableState.pipes;
const pipes = logger.winston()._readableState.pipes;
expect(pipes.constructor.name).toBe('Console');
expect(pipes.silent).toBe(true);
});
@@ -131,8 +141,9 @@ describe('Logger', () => {
it('Should allow to disable log rotation', () => {
settings.set(['advanced', 'log_rotation'], false);
const logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
const pipes = logger.winston._readableState.pipes;
const pipes = logger.winston()._readableState.pipes;
expect(pipes[1].constructor.name).toBe('File');
expect(pipes[1].maxFiles).toBeNull();
expect(pipes[1].tailable).toBeFalsy();
@@ -142,6 +153,7 @@ describe('Logger', () => {
it('Should allow to symlink logs to current directory', () => {
settings.set(['advanced', 'log_symlink_current'], true);
let logger = require('../lib/util/logger').default;
logger.init();
logger.logOutput();
expect(fs.readdirSync(dir.name).includes('current')).toBeTruthy()
@@ -151,23 +163,24 @@ describe('Logger', () => {
it('Log', () => {
const logger = require('../lib/util/logger').default;
const warn = jest.spyOn(logger.winston, 'warning');
logger.init();
const warn = jest.spyOn(logger.winston(), 'warning');
logger.warn('warn');
expect(warn).toHaveBeenCalledWith('warn');
const debug = jest.spyOn(logger.winston, 'debug');
const debug = jest.spyOn(logger.winston(), 'debug');
logger.debug('debug');
expect(debug).toHaveBeenCalledWith('debug');
const warning = jest.spyOn(logger.winston, 'warning');
const warning = jest.spyOn(logger.winston(), 'warning');
logger.warning('warning');
expect(warning).toHaveBeenCalledWith('warning');
const info = jest.spyOn(logger.winston, 'info');
const info = jest.spyOn(logger.winston(), 'info');
logger.info('info');
expect(info).toHaveBeenCalledWith('info');
const error = jest.spyOn(logger.winston, 'error');
const error = jest.spyOn(logger.winston(), 'error');
logger.error('error');
expect(error).toHaveBeenCalledWith('error');
});
+2 -2
View File
@@ -102,7 +102,7 @@ describe('Networkmap', () => {
let call = MQTT.publish.mock.calls[0];
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/networkmap/raw');
const expected = {"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Aqara double key wireless wall switch (2016 model)","model":"WXKG02LM_rev1","supports":"battery, action, voltage, linkquality","vendor":"Xiaomi"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":null,"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description": "external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]};
const expected = {"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Aqara double key wireless wall switch (2016 model)","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Xiaomi"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":null,"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description": "external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]};
expect(JSON.parse(call[1])).toStrictEqual(expected);
/**
@@ -276,7 +276,7 @@ describe('Networkmap', () => {
let call = MQTT.publish.mock.calls[0];
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
const expected = {"data":{"routes":true,"type":"raw","value":{"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Aqara double key wireless wall switch (2016 model)","model":"WXKG02LM_rev1","supports":"battery, action, voltage, linkquality","vendor":"Xiaomi"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":null,"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]}},"status":"ok"};
const expected = {"data":{"routes":true,"type":"raw","value":{"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Aqara double key wireless wall switch (2016 model)","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Xiaomi"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":null,"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]}},"status":"ok"};
const actual = JSON.parse(call[1]);
expect(actual).toStrictEqual(expected);
});
+4 -2
View File
@@ -48,6 +48,8 @@ describe('On event', () => {
expect(call[0]).toBe('start')
expect(call[1]).toStrictEqual({})
expect(call[2]).toBe(device);
expect(call[3]).toStrictEqual(settings.getDevice(device.ieeeAddr));
expect(call[4]).toStrictEqual({});
});
it('Should call with stop event', async () => {
@@ -66,7 +68,7 @@ describe('On event', () => {
await zigbeeHerdsman.events.deviceAnnounce({device});
await flushPromises();
expect(mockOnEvent).toHaveBeenCalledTimes(1);
expect(mockOnEvent).toHaveBeenCalledWith('deviceAnnounce', {device}, device, settings.getDevice(device.ieeeAddr));
expect(mockOnEvent).toHaveBeenCalledWith('deviceAnnounce', {device}, device, settings.getDevice(device.ieeeAddr), {});
});
it('Should call index onEvent with zigbee event', async () => {
@@ -74,6 +76,6 @@ describe('On event', () => {
await zigbeeHerdsman.events.deviceAnnounce({device});
await flushPromises();
expect(zigbeeHerdsmanConverters.onEvent).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsmanConverters.onEvent).toHaveBeenCalledWith('deviceAnnounce', {device}, device);
expect(zigbeeHerdsmanConverters.onEvent).toHaveBeenCalledWith('deviceAnnounce', {device}, device, settings.getDevice(device.ieeeAddr), {});
});
});
+4 -1
View File
@@ -1,3 +1,5 @@
const path = require('path');
const data = require('./stub/data');
const logger = require('./stub/logger');
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
@@ -11,6 +13,7 @@ const zigbeeOTA = require('zigbee-herdsman-converters/lib/ota/zigbeeOTA');
const spyUseIndexOverride = jest.spyOn(zigbeeOTA, 'useIndexOverride');
describe('OTA update', () => {
let controller;
@@ -468,7 +471,7 @@ describe('OTA update', () => {
it('Set zigbee_ota_override_index_location', async () => {
settings.set(['ota', 'zigbee_ota_override_index_location'], 'local.index.json');
await resetExtension();
expect(spyUseIndexOverride).toHaveBeenCalledWith(data.mockDir + '/local.index.json');
expect(spyUseIndexOverride).toHaveBeenCalledWith(path.join(data.mockDir, 'local.index.json'));
spyUseIndexOverride.mockClear();
settings.set(['ota', 'zigbee_ota_override_index_location'], 'http://my.site/index.json');
+4 -4
View File
@@ -919,11 +919,11 @@ describe('Publish', () => {
expect(endpoint.command).toHaveBeenCalledTimes(3);
expect(endpoint.command.mock.calls[0]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 50, transtime: 0}, {}]);
expect(endpoint.command.mock.calls[1]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 0, transtime: 0}, {}]);
expect(endpoint.command.mock.calls[2]).toEqual(["genOnOff", "on", {}, {}]);
expect(endpoint.command.mock.calls[2]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 50, transtime: 0}, {}]);
expect(MQTT.publish).toHaveBeenCalledTimes(3);
expect(MQTT.publish.mock.calls[0]).toEqual(["zigbee2mqtt/bulb_color", stringify({state: 'ON', brightness: 50}), {"qos": 0, "retain": false}, expect.any(Function)]);
expect(MQTT.publish.mock.calls[1]).toEqual(["zigbee2mqtt/bulb_color", stringify({state: 'OFF', brightness: 0}), {"qos": 0, "retain": false}, expect.any(Function)]);
expect(MQTT.publish.mock.calls[2]).toEqual(["zigbee2mqtt/bulb_color", stringify({state: 'ON', brightness: 1}), {"qos": 0, "retain": false}, expect.any(Function)]);
expect(MQTT.publish.mock.calls[2]).toEqual(["zigbee2mqtt/bulb_color", stringify({state: 'ON', brightness: 50}), {"qos": 0, "retain": false}, expect.any(Function)]);
});
it('Should turn device off when brightness 0 is send with transition', async () => {
@@ -938,11 +938,11 @@ describe('Publish', () => {
expect(endpoint.command).toHaveBeenCalledTimes(3);
expect(endpoint.command.mock.calls[0]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 50, transtime: 0}, {}]);
expect(endpoint.command.mock.calls[1]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 0, transtime: 30}, {}]);
expect(endpoint.command.mock.calls[2]).toEqual(["genOnOff", "on", {}, {}]);
expect(endpoint.command.mock.calls[2]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 50, transtime: 0}, {}]);
expect(MQTT.publish).toHaveBeenCalledTimes(3);
expect(MQTT.publish.mock.calls[0]).toEqual(["zigbee2mqtt/bulb_color", stringify({state: 'ON', brightness: 50}), {"qos": 0, "retain": false}, expect.any(Function)]);
expect(MQTT.publish.mock.calls[1]).toEqual(["zigbee2mqtt/bulb_color", stringify({state: 'OFF', brightness: 0}), {"qos": 0, "retain": false}, expect.any(Function)]);
expect(MQTT.publish.mock.calls[2]).toEqual(["zigbee2mqtt/bulb_color", stringify({state: 'ON', brightness: 1}), {"qos": 0, "retain": false}, expect.any(Function)]);
expect(MQTT.publish.mock.calls[2]).toEqual(["zigbee2mqtt/bulb_color", stringify({state: 'ON', brightness: 50}), {"qos": 0, "retain": false}, expect.any(Function)]);
});
it('Should allow to set color via hue and saturation', async () => {
+1 -37
View File
@@ -254,42 +254,6 @@ describe('Receive', () => {
expect(MQTT.publish.mock.calls[0][2]).toStrictEqual({"qos": 0, "retain": false});
});
it('Should handle a zigbee message with voltage 2850', async () => {
const device = zigbeeHerdsman.devices.WXKG02LM_rev1;
const data = {'65281': {'1': 2850}}
const payload = {data, cluster: 'genBasic', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
await zigbeeHerdsman.events.message(payload);
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/button_double_key');
expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({battery: 35, voltage: 2850});
expect(MQTT.publish.mock.calls[0][2]).toStrictEqual({"qos": 0, "retain": false});
});
it('Should handle a zigbee message with voltage 2650', async () => {
const device = zigbeeHerdsman.devices.WXKG02LM_rev1;
const data = {'65281': {'1': 2650}}
const payload = {data, cluster: 'genBasic', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
await zigbeeHerdsman.events.message(payload);
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/button_double_key');
expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({battery: 14, voltage: 2650});
expect(MQTT.publish.mock.calls[0][2]).toStrictEqual({"qos": 0, "retain": false});
});
it('Should handle a zigbee message with voltage 2000', async () => {
const device = zigbeeHerdsman.devices.WXKG02LM_rev1;
const data = {'65281': {'1': 2000}}
const payload = {data, cluster: 'genBasic', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
await zigbeeHerdsman.events.message(payload);
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/button_double_key');
expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({battery: 0, voltage: 2000});
expect(MQTT.publish.mock.calls[0][2]).toStrictEqual({"qos": 0, "retain": false});
});
it('Should publish 1 message when converted twice', async () => {
const device = zigbeeHerdsman.devices.RTCGQ11LM;
const data = {'65281': {'1': 3045, '3': 19, '4': 17320, '5': 35, '6': [0, 3], '10': 51107, '11': 381, '100': 0}}
@@ -298,7 +262,7 @@ describe('Receive', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/occupancy_sensor');
expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({'battery': 100, 'illuminance': 381, "illuminance_lux": 381, 'voltage': 3045, 'temperature': 19});
expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({'battery': 100, 'illuminance': 381, "illuminance_lux": 381, 'voltage': 3045, 'device_temperature': 19, 'power_outage_count': 34});
expect(MQTT.publish.mock.calls[0][2]).toStrictEqual({"qos": 0, "retain": false});
});
+47 -3
View File
@@ -39,6 +39,7 @@ describe('Settings', () => {
beforeEach(() => {
remove(configurationFile);
remove(secretFile);
remove(devicesFile);
remove(groupsFile);
clearEnvironmentVariables();
@@ -171,6 +172,49 @@ describe('Settings', () => {
expect(read(secretFile)).toStrictEqual({...contentSecret, username: 'test123', network_key: [1,2,3,4]});
});
it('Should read ALL secrets form a separate file', () => {
const contentConfiguration = {
mqtt: {
server: '!secret server',
user: '!secret username',
password: '!secret password',
},
advanced: {
network_key: '!secret network_key',
}
};
const contentSecret = {
server: 'my.mqtt.server',
username: 'mysecretusername',
password: 'mysecretpassword',
network_key: [1,2,3],
};
write(secretFile, contentSecret, false);
write(configurationFile, contentConfiguration);
const expected = {
base_topic: 'zigbee2mqtt',
include_device_information: false,
force_disable_retain: false,
password: "mysecretpassword",
server: "my.mqtt.server",
user: "mysecretusername",
};
expect(settings.get().mqtt).toStrictEqual(expected);
expect(settings.get().advanced.network_key).toStrictEqual([1,2,3]);
settings.testing.write();
expect(read(configurationFile)).toStrictEqual(contentConfiguration);
expect(read(secretFile)).toStrictEqual(contentSecret);
settings.set(['mqtt', 'server'], 'not.secret.server');
expect(read(configurationFile)).toStrictEqual(contentConfiguration);
expect(read(secretFile)).toStrictEqual({...contentSecret, server: 'not.secret.server'});
});
it('Should read devices form a separate file', () => {
const contentConfiguration = {
devices: 'devices.yaml',
@@ -747,15 +791,15 @@ describe('Settings', () => {
expect(settings.validate()).toEqual(expect.arrayContaining([error]));
});
it('Configuration friendly name cannot contain null char', async () => {
it('Configuration friendly name cannot contain control char', async () => {
write(configurationFile, {
...minimalConfig,
devices: {'0x0017880104e45519': {friendly_name: 'blaa/blaa' + String.fromCharCode(0), retain: false}},
devices: {'0x0017880104e45519': {friendly_name: 'blaa/blaa\u009f', retain: false}},
});
settings.reRead();
const error = `friendly_name is not allowed to contain null char`;
const error = `friendly_name is not allowed to contain control char`;
expect(settings.validate()).toEqual(expect.arrayContaining([error]));
});
+5 -1
View File
@@ -21,7 +21,8 @@ function writeDefaultConfiguration() {
devices: {
"0x000b57fffec6a5b2": {
retain: true,
friendly_name: "bulb"
friendly_name: "bulb",
description: "this is my bulb",
},
"0x0017880104e45517": {
retain: true,
@@ -242,6 +243,9 @@ const defaultState = {
"0x0017880104e45517": {
"brightness": 255
},
"1": {
'state': 'ON',
}
}
function getDefaultState() {
+1
View File
@@ -12,6 +12,7 @@ const callTransports = (level, message) => {
}
const mock = {
init: jest.fn(),
info: jest.fn().mockImplementation((msg) => callTransports('info', msg)),
warn: jest.fn().mockImplementation((msg) => callTransports('warn', msg)),
error: jest.fn().mockImplementation((msg) => callTransports('error', msg)),
+2 -1
View File
@@ -188,7 +188,7 @@ const devices = {
'MKS-CM-W5': new Device('Router', '0x90fd9ffffe4b64ac', 33901, 4476, [new Endpoint(1, [0,4,3,5,10,258,13,19,6,1,1030,8,768,1027,1029,1026], [0,3,4,6,8,5], '0x90fd9ffffe4b64aa', [], {})], true, "Mains (single phase)", "qnazj70", false),
'GL-S-007ZS': new Device('Router', '0x0017880104e45526', 6540,4151, [new Endpoint(1, [0], [], '0x0017880104e45526')], true, "Mains (single phase)", 'GL-S-007ZS'),
'U202DST600ZB': new Device('Router', '0x0017880104e43559', 6540,4151, [new Endpoint(10, [0, 6], [], '0x0017880104e43559'), new Endpoint(11, [0, 6], [], '0x0017880104e43559')], true, "Mains (single phase)", 'U202DST600ZB'),
'3157100': new Device('Router', '0x0017880104e44559', 6542,4151, [new Endpoint(1, [], [], '0x0017880104e44559')], true, "Mains (single phase)", '3157100'),
'3157100': new Device('Router', '0x0017880104e44559', 6542,4151, [new Endpoint(1, [], [], '0x0017880104e44559')], true, "Mains (single phase)", '3157100', false, 'Centralite'),
'J1': new Device('Router', '0x0017880104a44559', 6543,4151, [new Endpoint(1, [], [], '0x0017880104a44559')], true, "Mains (single phase)", 'J1 (5502)'),
'TS0601_thermostat': TS0601_thermostat,
'external_converter_device': new Device('EndDevice', '0x0017880104e45511', 1114, 'external', [new Endpoint(1, [], [], '0x0017880104e45511')], false, null, 'external_converter_device' ),
@@ -207,6 +207,7 @@ const mock = {
touchlinkScan: jest.fn(),
touchlinkIdentify: jest.fn(),
start: jest.fn(),
backup: jest.fn(),
isStopping: jest.fn(),
permitJoin: jest.fn(),
getCoordinatorVersion: jest.fn().mockReturnValue({type: 'z-Stack', meta: {version: 1, revision: 20190425}}),
-1
View File
@@ -18,7 +18,6 @@ echo "Creating backup of configuration..."
cp -R data data-backup
echo "Updating..."
git checkout HEAD -- npm-shrinkwrap.json
git pull
echo "Installing dependencies..."