Compare commits

..
55 Commits
Author SHA1 Message Date
Koen Kanters 7ee207fc0e chore(dev): release 1.33.0 (#18490) 2023-09-01 15:57:57 +02:00
Koen KantersandKoenkk e78aa3589b fix(ignore): update zigbee-herdsman-converters to 15.67.1 (#18817)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-09-01 15:50:16 +02:00
Koen KantersandKoenkk 68169f0e50 fix(ignore): update zigbee-herdsman to 0.18.5 (#18816)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-09-01 13:45:06 +00:00
Koen KantersandKoenkk 97f6a840e4 fix(ignore): update zigbee-herdsman-converters to 15.67.0 (#18815)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-09-01 13:35:18 +00:00
Koen Kantersandnurikk 9a6445a8a0 fix(ignore): update zigbee2mqtt-frontend to 0.6.135 (#18814)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2023-09-01 15:27:52 +02:00
Koen KantersandKoenkk 0ec68a4b71 fix(ignore): update zigbee-herdsman-converters to 15.66.1 (#18790)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-31 07:35:19 +00:00
Koen KantersandKoenkk 5084b1293e fix(ignore): update zigbee-herdsman-converters to 15.64.0 (#18784)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-29 06:46:11 +00:00
Koen Kantersandnurikk 4949a54d66 fix(ignore): update zigbee2mqtt-frontend to 0.6.134 (#18761)
Co-authored-by: nurikk <nurikk@users.noreply.github.com>
2023-08-27 10:59:06 +00:00
Koen KantersandKoenkk b193d89042 fix(ignore): update zigbee-herdsman-converters to 15.63.0 (#18758)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-27 06:42:34 +00:00
Koen KantersandKoenkk bc17e6406b fix(ignore): update dependencies (#18755)
* fix(ignore): update dependencies

* lock version

---------

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-27 08:36:28 +02:00
Yip Rui Fung d1e50ce534 fix: Use QOS1 for rarely sent discovery and availability messages (#18756)
* Use QOS1 for rarely sent messages

Specifically discovery and availability depending on the set up, it's possible for these messages to be silently discarded by the broker.

Sending them as QOS1 means the mqtt library used will resend them until it gets an ACK.
Ensuring delivery is particularly important for messages that are rarely sent like discovery or availability as they are only sent when there are changes, and nondelivery or dropped messages for them impede proper functioning in home assistant.

* Fix tests for discovery and availability using QOS1
2023-08-27 06:26:07 +00:00
koenkk 677db105e2 chore: remove duplicate object_id warning for function expose 2023-08-26 08:43:47 +02:00
Koen KantersandKoenkk 514d9f38b2 fix(ignore): update zigbee-herdsman-converters to 15.62.0 (#18742)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-26 06:02:23 +00:00
Koen Kanters 704ecf366e feat: Add Home Assistant origin to discovery (#18741) 2023-08-26 07:56:31 +02:00
Koen KantersandKoenkk 1439f4c6ac fix(ignore): update zigbee-herdsman to 0.18.4 (#18727)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-25 07:46:49 +00:00
Koen KantersandKoenkk 6a1b6a1403 fix(ignore): update zigbee-herdsman to 0.18.3 (#18714)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-24 08:27:14 +00:00
Koen KantersandKoenkk 239c8fc70c Update zigbee-herdsman-converters to 15.61.0 (#18713)
* fix(ignore): update zigbee-herdsman-converters to 15.61.0

* u

* u

---------

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-24 08:20:28 +00:00
Artem Draft e33e335c0c feat: Use labels in Home Assistant entity naming (#18678)
* feat: Use labels in Home Assistant entity naming

* tweak comment
2023-08-22 08:07:23 +02:00
Koen KantersandKoenkk 71c4211d13 fix(ignore): update zigbee-herdsman-converters to 15.60.0 (#18672)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-20 15:58:16 +00:00
4b3a2c9f5a feat: Support scene renaming (#18667)
Co-authored-by: Frank van Viegen <frank@vanviegen.net>
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
2023-08-20 17:50:10 +02:00
Koen KantersandKoenkk 7185d9e501 fix(ignore): update zigbee-herdsman-converters to 15.59.0 (#18661)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-19 07:17:20 +00:00
Koen KantersandKoenkk dcbf4e5b0e fix(ignore): update zigbee-herdsman-converters to 15.58.1 (#18658)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-18 18:08:05 +00:00
Koen KantersandKoenkk db812cd729 fix(ignore): update zigbee-herdsman to 0.18.2 (#18648)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-17 19:59:06 +00:00
Koen KantersandKoenkk c214f83298 Update zigbee-herdsman-converters to 15.58.0 (#18647)
* fix(ignore): update zigbee-herdsman-converters to 15.58.0

* fix tests

---------

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-17 19:24:20 +00:00
Koen KantersandKoenkk 3736b5901b fix(ignore): update zigbee-herdsman to 0.18.1 (#18627)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-15 18:38:11 +00:00
Koen Kanters 3af130a2c0 feat: Support coordinator check (#18599)
* feat: Support coordinator check

* add tests
2023-08-14 20:35:15 +02:00
Koen KantersandKoenkk 145bc05dc3 fix(ignore): update zigbee-herdsman-converters to 15.57.0 (#18616)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-14 17:02:24 +00:00
Koen KantersandKoenkk cc0536c5ab fix(ignore): update zigbee-herdsman to 0.18.0 (#18600)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-13 07:13:00 +00:00
Koen KantersandKoenkk 0d00a144f5 fix(ignore): update dependencies (#18598)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-13 08:45:32 +02:00
Koen Kanters aa021d988d feat: Support generating extended_pan_id (#18596)
* feat: Support generating extended_pan_id

* Update configuration.example.yaml
2023-08-12 15:07:30 +02:00
Koen KantersandKoenkk ea147900ab Update zigbee-herdsman-converters to 15.56.0 (#18593)
* fix(ignore): update zigbee-herdsman-converters to 15.56.0

* Update bridge.test.js

---------

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-12 12:39:00 +00:00
Koen KantersandKoenkk b1796e3e1a fix(ignore): update zigbee-herdsman-converters to 15.55.3 (#18578)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-10 16:23:27 +00:00
koenkk b55cc0a037 fix(ignore): chore: Update dependencies (#18459) 2023-08-09 20:04:20 +02:00
Koen KantersandKoenkk 8675b1c9ed fix(ignore): update zigbee-herdsman-converters to 15.55.2 (#18566)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-09 18:00:54 +00:00
koenkk b09b8ec3d6 fix(ignore): Revert "chore: Update dependencies (#18459)"
This reverts commit 53cb42103c.
2023-08-09 07:33:27 +02:00
Koen KantersandKoenkk 7f05f31c72 fix(ignore): update zigbee-herdsman-converters to 15.55.1 (#18557)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-08 18:10:21 +00:00
Koen KantersandKoenkk 53cb42103c chore: Update dependencies (#18459)
* fix(ignore): update dependencies

* Make compatible with MQTT 5

* updates

---------

Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-08 20:04:22 +02:00
Koen KantersandKoenkk a892a1b898 fix(ignore): update zigbee-herdsman to 0.17.3 (#18556)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-08 17:49:39 +00:00
Koen Kanters 1f52faee39 feat: Drop Node.js 14 support (#18555) 2023-08-08 19:40:22 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> e0038380c7 chore: Bump alpine from 3.18.2 to 3.18.3 in /docker (#18554)
Bumps alpine from 3.18.2 to 3.18.3.

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-08 19:37:05 +02:00
Koen KantersandKoenkk 6050428566 fix(ignore): update zigbee-herdsman-converters to 15.55.0 (#18547)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-07 17:03:41 +00:00
Koen KantersandKoenkk b5e9ed436c fix(ignore): update zigbee-herdsman-converters to 15.54.1 (#18537)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-06 07:14:43 +00:00
Koen Kanters cf313c503b fix: Generate pan_id for new installations (#18530) 2023-08-05 13:22:31 +02:00
Koen KantersandKoenkk abba8b3ba8 fix(ignore): update zigbee-herdsman-converters to 15.54.0 (#18527)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-05 06:40:44 +00:00
Koen KantersandKoenkk f08cd81ac0 fix(ignore): update zigbee-herdsman-converters to 15.53.0 (#18524)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-04 17:37:59 +00:00
Koen Kanters 68ec507e30 fix: Add secure default config (#18357)
* fix: Add secure default config. https://github.com/Koenkk/zigbee2mqtt/issues/16188

* Enable frontend by default
2023-08-03 20:48:23 +02:00
Koen KantersandKoenkk 0718ca162d fix(ignore): update zigbee-herdsman-converters to 15.52.0 (#18514)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-03 18:39:15 +00:00
Koen KantersandKoenkk 2d917a7d0d fix(ignore): update zigbee-herdsman-converters to 15.51.0 (#18501)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2023-08-02 18:09:54 +00:00
koenkk ed22ecedbf fix: Fix Home Assistant TypeError: object of type 'NoneType' has no len() error. https://github.com/Koenkk/zigbee2mqtt/issues/17861 2023-08-02 19:34:39 +02:00
Koen Kanters f6196107a7 fix: Fix Home Assistant `MQTT device name is equal to entity name in your config (#18491)
* fix: Fix Home Assistant `MQTT device name is equal to entity name in your config` error. https://github.com/Koenkk/zigbee2mqtt/issues/18445

* remove onlythis
2023-08-01 20:39:02 +02:00
koenkk 49920ce093 chore: use .md extension for changelog 2023-08-01 20:15:01 +02:00
koenkk 33543c8588 chore: use GH_TOKEN for gist edit 2023-08-01 20:07:05 +02:00
koenkk c1413afa04 chore: publish changelog in gist 2023-08-01 20:04:14 +02:00
Koen Kanters df93e4c8f9 feat: Let Home Assistant generate entity name when device_class is present (#18208) 2023-08-01 19:46:24 +02:00
koenkk 7709af8a5e chore: promote to dev 2023-08-01 19:25:37 +02:00
27 changed files with 2085 additions and 1772 deletions
+5 -7
View File
@@ -56,21 +56,19 @@ jobs:
wget -q -O - https://raw.githubusercontent.com/Koenkk/zigbee2mqtt/release-please--branches--dev--components--release-please-action/CHANGELOG.md > z2m/CHANGELOG.md || true
cd z2m
npm ci
node scripts/generateChangelog.js $MASTER_Z2M_VERSION $MASTER_ZHC_VERSION $MASTER_ZH_VERSION >> ../changelog.txt
node scripts/generateChangelog.js $MASTER_Z2M_VERSION $MASTER_ZHC_VERSION $MASTER_ZH_VERSION >> ../changelog.md
env:
GH_TOKEN: ${{secrets.GH_TOKEN}}
- name: Update changelog in release PR
run: |
PR=$(echo '${{ steps.release.outputs.pr }}' | jq -r .number)
cd z2m
gh pr edit $PR -F ../changelog.txt
gh gist edit bfd4c3d1725a2cccacc11d6ba51008ba -a changelog.md
env:
GH_TOKEN: ${{ github.token }}
GH_TOKEN: ${{secrets.GH_TOKEN}}
- name: Upload changelog
uses: actions/upload-artifact@v3
with:
name: changelog
path: changelog.txt
path: changelog.md
- name: Save cache commit-user-lookup.json
uses: actions/cache/save@v3
if: always()
@@ -95,7 +93,7 @@ jobs:
- uses: ncipollo/release-action@v1
name: Create release
with:
bodyFile: ../changelog/changelog.txt
bodyFile: ../changelog/changelog.md
draft: true
name: ${{ needs.release-please.outputs.version }}
tag: ${{ needs.release-please.outputs.version }}
+1
View File
@@ -80,6 +80,7 @@ data/extension
# Ignore config
data/*.yaml
!data/configuration.example.yaml
# commit-user-lookup.json
scripts/commit-user-lookup.json
+1 -1
View File
@@ -1,4 +1,4 @@
{
".": "1.32.2",
".": "1.33.0",
"bootstrap-sha": "b2dd21e164b457d5cd0c9d455cdb879d881c44bd"
}
+54
View File
@@ -1,5 +1,59 @@
# Changelog
## [1.33.0](https://github.com/Koenkk/zigbee2mqtt/compare/1.32.2...1.33.0) (2023-09-01)
### Features
* Add Home Assistant origin to discovery ([#18741](https://github.com/Koenkk/zigbee2mqtt/issues/18741)) ([704ecf3](https://github.com/Koenkk/zigbee2mqtt/commit/704ecf366e51ff777e96379acbb89d9c3c416d6d))
* Drop Node.js 14 support ([#18555](https://github.com/Koenkk/zigbee2mqtt/issues/18555)) ([1f52fae](https://github.com/Koenkk/zigbee2mqtt/commit/1f52faee398e720ad64e61b559e04be81b58b979))
* Let Home Assistant generate entity name when device_class is present ([#18208](https://github.com/Koenkk/zigbee2mqtt/issues/18208)) ([df93e4c](https://github.com/Koenkk/zigbee2mqtt/commit/df93e4c8f9490e503e5896e082fc2578f98c2e0e))
* Support coordinator check ([#18599](https://github.com/Koenkk/zigbee2mqtt/issues/18599)) ([3af130a](https://github.com/Koenkk/zigbee2mqtt/commit/3af130a2c056aab74d793a963aceead348f56058))
* Support generating extended_pan_id ([#18596](https://github.com/Koenkk/zigbee2mqtt/issues/18596)) ([aa021d9](https://github.com/Koenkk/zigbee2mqtt/commit/aa021d988db31ef206a9bfa6b5312d631bb3de79))
* Support scene renaming ([#18667](https://github.com/Koenkk/zigbee2mqtt/issues/18667)) ([4b3a2c9](https://github.com/Koenkk/zigbee2mqtt/commit/4b3a2c9f5a74ec25fd82d219a7fc31cddb7c7def))
* Use labels in Home Assistant entity naming ([#18678](https://github.com/Koenkk/zigbee2mqtt/issues/18678)) ([e33e335](https://github.com/Koenkk/zigbee2mqtt/commit/e33e335c0ca92670eee4be672ec53282cc2b79d3))
### Bug Fixes
* Add secure default config ([#18357](https://github.com/Koenkk/zigbee2mqtt/issues/18357)) ([68ec507](https://github.com/Koenkk/zigbee2mqtt/commit/68ec507e30bead5e9263c0fdc4c47e18c257d82f))
* Fix Home Assistant `MQTT device name is equal to entity name in your config ([#18491](https://github.com/Koenkk/zigbee2mqtt/issues/18491)) ([f619610](https://github.com/Koenkk/zigbee2mqtt/commit/f6196107a75f9257366505a3ad5ef7b29ff5bc1c))
* Fix Home Assistant `TypeError: object of type 'NoneType' has no len()` error. https://github.com/Koenkk/zigbee2mqtt/issues/17861 ([ed22ece](https://github.com/Koenkk/zigbee2mqtt/commit/ed22ecedbfd51fc2f5572930aacd37e5b48e04cf))
* Generate pan_id for new installations ([#18530](https://github.com/Koenkk/zigbee2mqtt/issues/18530)) ([cf313c5](https://github.com/Koenkk/zigbee2mqtt/commit/cf313c503b7b070e24739a1f5139e5833405eebc))
* **ignore:** chore: Update dependencies ([#18459](https://github.com/Koenkk/zigbee2mqtt/issues/18459)) ([b55cc0a](https://github.com/Koenkk/zigbee2mqtt/commit/b55cc0a037ccb2c16ac2147ae7e1fb2e4d596806))
* **ignore:** Revert "chore: Update dependencies ([#18459](https://github.com/Koenkk/zigbee2mqtt/issues/18459))" ([b09b8ec](https://github.com/Koenkk/zigbee2mqtt/commit/b09b8ec3d6b30b0bc34c36819e39298ec5a94c63))
* **ignore:** update dependencies ([#18598](https://github.com/Koenkk/zigbee2mqtt/issues/18598)) ([0d00a14](https://github.com/Koenkk/zigbee2mqtt/commit/0d00a144f5c40bf1fce30f9ee65335a6d0076972))
* **ignore:** update dependencies ([#18755](https://github.com/Koenkk/zigbee2mqtt/issues/18755)) ([bc17e64](https://github.com/Koenkk/zigbee2mqtt/commit/bc17e6406bdd4104ba7f1b4c7663a18f82fee35b))
* **ignore:** update zigbee-herdsman to 0.17.3 ([#18556](https://github.com/Koenkk/zigbee2mqtt/issues/18556)) ([a892a1b](https://github.com/Koenkk/zigbee2mqtt/commit/a892a1b898f8cf4fc02c1ec7d9159d426c5ea547))
* **ignore:** update zigbee-herdsman to 0.18.0 ([#18600](https://github.com/Koenkk/zigbee2mqtt/issues/18600)) ([cc0536c](https://github.com/Koenkk/zigbee2mqtt/commit/cc0536c5ab1d5f04b75bf9cd21c4f9579c60dc31))
* **ignore:** update zigbee-herdsman to 0.18.1 ([#18627](https://github.com/Koenkk/zigbee2mqtt/issues/18627)) ([3736b59](https://github.com/Koenkk/zigbee2mqtt/commit/3736b5901b9f497b74a2efa57adb105802ddc153))
* **ignore:** update zigbee-herdsman to 0.18.2 ([#18648](https://github.com/Koenkk/zigbee2mqtt/issues/18648)) ([db812cd](https://github.com/Koenkk/zigbee2mqtt/commit/db812cd72918eb0438129639c9d7a8af86f8fc37))
* **ignore:** update zigbee-herdsman to 0.18.3 ([#18714](https://github.com/Koenkk/zigbee2mqtt/issues/18714)) ([6a1b6a1](https://github.com/Koenkk/zigbee2mqtt/commit/6a1b6a14038290fc9d13008b09da002fd55f540f))
* **ignore:** update zigbee-herdsman to 0.18.4 ([#18727](https://github.com/Koenkk/zigbee2mqtt/issues/18727)) ([1439f4c](https://github.com/Koenkk/zigbee2mqtt/commit/1439f4c6ace962f8b14ec43a8b418ca12a4db27f))
* **ignore:** update zigbee-herdsman to 0.18.5 ([#18816](https://github.com/Koenkk/zigbee2mqtt/issues/18816)) ([68169f0](https://github.com/Koenkk/zigbee2mqtt/commit/68169f0e50290c8c2fa5fba052b4446c9a46d2f1))
* **ignore:** update zigbee-herdsman-converters to 15.51.0 ([#18501](https://github.com/Koenkk/zigbee2mqtt/issues/18501)) ([2d917a7](https://github.com/Koenkk/zigbee2mqtt/commit/2d917a7d0db1fc4d89ba163d63e2d2932d0655fe))
* **ignore:** update zigbee-herdsman-converters to 15.52.0 ([#18514](https://github.com/Koenkk/zigbee2mqtt/issues/18514)) ([0718ca1](https://github.com/Koenkk/zigbee2mqtt/commit/0718ca162dcf6ab41f192e4dd8ecc83b38e25410))
* **ignore:** update zigbee-herdsman-converters to 15.53.0 ([#18524](https://github.com/Koenkk/zigbee2mqtt/issues/18524)) ([f08cd81](https://github.com/Koenkk/zigbee2mqtt/commit/f08cd81ac0ad4436883acf39947e7254002ef9fe))
* **ignore:** update zigbee-herdsman-converters to 15.54.0 ([#18527](https://github.com/Koenkk/zigbee2mqtt/issues/18527)) ([abba8b3](https://github.com/Koenkk/zigbee2mqtt/commit/abba8b3ba8c31c2b9057f74e455824045082a4ec))
* **ignore:** update zigbee-herdsman-converters to 15.54.1 ([#18537](https://github.com/Koenkk/zigbee2mqtt/issues/18537)) ([b5e9ed4](https://github.com/Koenkk/zigbee2mqtt/commit/b5e9ed436cd3a8e5b8183fe44685d2f41422b2fd))
* **ignore:** update zigbee-herdsman-converters to 15.55.0 ([#18547](https://github.com/Koenkk/zigbee2mqtt/issues/18547)) ([6050428](https://github.com/Koenkk/zigbee2mqtt/commit/60504285663e346c2078819c6445905fb2aaff1c))
* **ignore:** update zigbee-herdsman-converters to 15.55.1 ([#18557](https://github.com/Koenkk/zigbee2mqtt/issues/18557)) ([7f05f31](https://github.com/Koenkk/zigbee2mqtt/commit/7f05f31c72cfd394cbe0d8e20de5c9f5d245db85))
* **ignore:** update zigbee-herdsman-converters to 15.55.2 ([#18566](https://github.com/Koenkk/zigbee2mqtt/issues/18566)) ([8675b1c](https://github.com/Koenkk/zigbee2mqtt/commit/8675b1c9ed405d7abe42f6725c0627968c533f4b))
* **ignore:** update zigbee-herdsman-converters to 15.55.3 ([#18578](https://github.com/Koenkk/zigbee2mqtt/issues/18578)) ([b1796e3](https://github.com/Koenkk/zigbee2mqtt/commit/b1796e3e1a1d58c79dda39a8041ce67ceea133a4))
* **ignore:** update zigbee-herdsman-converters to 15.57.0 ([#18616](https://github.com/Koenkk/zigbee2mqtt/issues/18616)) ([145bc05](https://github.com/Koenkk/zigbee2mqtt/commit/145bc05dc30d952e919035a7b741f91ee98a8997))
* **ignore:** update zigbee-herdsman-converters to 15.58.1 ([#18658](https://github.com/Koenkk/zigbee2mqtt/issues/18658)) ([dcbf4e5](https://github.com/Koenkk/zigbee2mqtt/commit/dcbf4e5b0e75d0e50688434079588fe179406520))
* **ignore:** update zigbee-herdsman-converters to 15.59.0 ([#18661](https://github.com/Koenkk/zigbee2mqtt/issues/18661)) ([7185d9e](https://github.com/Koenkk/zigbee2mqtt/commit/7185d9e5013888f8276e74849c7463560c46f369))
* **ignore:** update zigbee-herdsman-converters to 15.60.0 ([#18672](https://github.com/Koenkk/zigbee2mqtt/issues/18672)) ([71c4211](https://github.com/Koenkk/zigbee2mqtt/commit/71c4211d13df29aefb217a030c5e114c7a666112))
* **ignore:** update zigbee-herdsman-converters to 15.62.0 ([#18742](https://github.com/Koenkk/zigbee2mqtt/issues/18742)) ([514d9f3](https://github.com/Koenkk/zigbee2mqtt/commit/514d9f38b2a7a46fe766dd422f540350bbbe0f5e))
* **ignore:** update zigbee-herdsman-converters to 15.63.0 ([#18758](https://github.com/Koenkk/zigbee2mqtt/issues/18758)) ([b193d89](https://github.com/Koenkk/zigbee2mqtt/commit/b193d8904299a0c5673da3b9185200ef6a3ed368))
* **ignore:** update zigbee-herdsman-converters to 15.64.0 ([#18784](https://github.com/Koenkk/zigbee2mqtt/issues/18784)) ([5084b12](https://github.com/Koenkk/zigbee2mqtt/commit/5084b1293e2ea857b363837eabf7ca0087f2607c))
* **ignore:** update zigbee-herdsman-converters to 15.66.1 ([#18790](https://github.com/Koenkk/zigbee2mqtt/issues/18790)) ([0ec68a4](https://github.com/Koenkk/zigbee2mqtt/commit/0ec68a4b71208fe42ea011e0084c8af0b187a261))
* **ignore:** update zigbee-herdsman-converters to 15.67.0 ([#18815](https://github.com/Koenkk/zigbee2mqtt/issues/18815)) ([97f6a84](https://github.com/Koenkk/zigbee2mqtt/commit/97f6a840e47612e98f20ba355dae7fb3dfd0868c))
* **ignore:** update zigbee-herdsman-converters to 15.67.1 ([#18817](https://github.com/Koenkk/zigbee2mqtt/issues/18817)) ([e78aa35](https://github.com/Koenkk/zigbee2mqtt/commit/e78aa3589b4c468716917f9f734f03801f86c3cd))
* **ignore:** update zigbee2mqtt-frontend to 0.6.134 ([#18761](https://github.com/Koenkk/zigbee2mqtt/issues/18761)) ([4949a54](https://github.com/Koenkk/zigbee2mqtt/commit/4949a54d664083f6152bafa78c5468f9a2c50f2b))
* **ignore:** update zigbee2mqtt-frontend to 0.6.135 ([#18814](https://github.com/Koenkk/zigbee2mqtt/issues/18814)) ([9a6445a](https://github.com/Koenkk/zigbee2mqtt/commit/9a6445a8a024c8b415caa9cd08e48df12a46e83b))
* Use QOS1 for rarely sent discovery and availability messages ([#18756](https://github.com/Koenkk/zigbee2mqtt/issues/18756)) ([d1e50ce](https://github.com/Koenkk/zigbee2mqtt/commit/d1e50ce534b906f426b28f2aaf8b0a51a97de264))
## [1.32.2](https://github.com/Koenkk/zigbee2mqtt/compare/1.32.1...1.32.2) (2023-08-01)
+29
View File
@@ -0,0 +1,29 @@
# Home Assistant integration (MQTT discovery)
homeassistant: false
# Enable the frontend, runs on port 8080 by default
frontend: true
# MQTT settings
mqtt:
# MQTT base topic for zigbee2mqtt MQTT messages
base_topic: zigbee2mqtt
# MQTT server URL
server: 'mqtt://localhost'
# MQTT server authentication, uncomment if required:
# user: my_user
# password: my_password
# Serial settings
serial:
# Location of CC2531 USB sniffer
port: /dev/ttyACM0
# Advanced settings
advanced:
# Let Zigbee2MQTT generate a network key on first start
network_key: GENERATE
# Let Zigbee2MQTT generate a pan_id on first start
pan_id: GENERATE
# Let Zigbee2MQTT generate a ext_pan_id on first start
ext_pan_id: GENERATE
+1 -1
View File
@@ -1,4 +1,4 @@
FROM alpine:3.18.2 as base
FROM alpine:3.18.3 as base
WORKDIR /app
RUN apk add --no-cache tzdata eudev tini nodejs
+1 -1
View File
@@ -11,7 +11,7 @@ echo "Using '$DATA' as data directory"
if [ ! -f "$DATA/configuration.yaml" ]; then
echo "Creating configuration file..."
cp /app/configuration.yaml "$DATA/configuration.yaml"
cp /app/configuration.example.yaml "$DATA/configuration.yaml"
fi
exec "$@"
+2 -2
View File
@@ -105,7 +105,7 @@ export default class Availability extends Extension {
override async start(): Promise<void> {
this.eventBus.onEntityRenamed(this, (data) => {
if (utils.isAvailabilityEnabledForEntity(data.entity, settings.get())) {
this.mqtt.publish(`${data.from}/availability`, null, {retain: true, qos: 0});
this.mqtt.publish(`${data.from}/availability`, null, {retain: true, qos: 1});
this.publishAvailability(data.entity, false, true);
}
});
@@ -164,7 +164,7 @@ export default class Availability extends Extension {
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});
this.mqtt.publish(topic, payload, {retain: true, qos: 1});
if (!skipGroups && entity.isDevice()) {
this.zigbee.groups().filter((g) => g.hasMember(entity))
+9
View File
@@ -46,6 +46,7 @@ export default class Bridge extends Extension {
'install_code/add': this.installCodeAdd,
'touchlink/scan': this.touchlinkScan,
'health_check': this.healthCheck,
'coordinator_check': this.coordinatorCheck,
'options': this.bridgeOptions,
// Below are deprecated
'config/last_seen': this.configLastSeen,
@@ -181,6 +182,14 @@ export default class Bridge extends Extension {
return utils.getResponse(message, {healthy: true}, null);
}
@bind async coordinatorCheck(message: string | KeyValue): Promise<MQTTResponse> {
const result = await this.zigbee.coordinatorCheck();
const missingRouters = result.missingRouters.map((d) => {
return {ieee_address: d.ieeeAddr, friendly_name: d.name};
});
return utils.getResponse(message, {missing_routers: missingRouters}, null);
}
@bind async groupAdd(message: string | KeyValue): Promise<MQTTResponse> {
if (typeof message === 'object' && !message.hasOwnProperty('friendly_name')) {
throw new Error(`Invalid payload`);
+1 -1
View File
@@ -124,7 +124,7 @@ export default class Frontend extends Extension {
if (!isBinary && data) {
const message = data.toString();
const {topic, payload} = JSON.parse(message);
this.mqtt.onMessage(`${this.mqttBaseTopic}/${topic}`, stringify(payload));
this.mqtt.onMessage(`${this.mqttBaseTopic}/${topic}`, Buffer.from(stringify(payload)));
}
});
+54 -12
View File
@@ -16,6 +16,7 @@ const sensorClick: DiscoveryEntry = {
object_id: 'click',
mockProperties: [{property: 'click', value: null}],
discovery_payload: {
name: 'Click',
icon: 'mdi:toggle-switch',
value_template: '{{ value_json.click }}',
},
@@ -42,6 +43,7 @@ const legacyMapping = [
mockProperties: [{property: 'brightness', value: null}],
object_id: 'brightness',
discovery_payload: {
name: 'Brightness',
unit_of_measurement: 'brightness',
icon: 'mdi:brightness-5',
value_template: '{{ value_json.brightness }}',
@@ -69,6 +71,7 @@ export default class HomeAssistant extends Extension {
private statusTopic = settings.get().homeassistant.status_topic;
private entityAttributes = settings.get().homeassistant.legacy_entity_attributes;
private zigbee2MQTTVersion: string;
private discoveryOrigin: {name: string, sw: string, url: string};
constructor(zigbee: Zigbee, mqtt: MQTT, state: State, publishEntityState: PublishEntityState,
eventBus: EventBus, enableDisableExtension: (enable: boolean, name: string) => Promise<void>,
@@ -85,6 +88,7 @@ export default class HomeAssistant extends Extension {
}
this.zigbee2MQTTVersion = (await utils.getZigbee2MQTTVersion(false)).version;
this.discoveryOrigin = {name: 'Zigbee2MQTT', sw: this.zigbee2MQTTVersion, url: 'https://www.zigbee2mqtt.io'};
this.eventBus.onDeviceRemoved(this, this.onDeviceRemoved);
this.eventBus.onMQTTMessage(this, this.onMQTTMessage);
@@ -142,6 +146,7 @@ export default class HomeAssistant extends Extension {
object_id: endpoint ? `light_${endpoint}` : 'light',
mockProperties: [{property: state.property, value: null}],
discovery_payload: {
name: endpoint ? utils.capitalize(endpoint) : null,
brightness: !!hasBrightness,
schema: 'json',
command_topic: true,
@@ -187,6 +192,7 @@ export default class HomeAssistant extends Extension {
object_id: endpoint ? `switch_${endpoint}` : 'switch',
mockProperties: [{property: property, value: null}],
discovery_payload: {
name: endpoint ? utils.capitalize(endpoint) : null,
payload_off: state.value_off,
payload_on: state.value_on,
value_template: `{{ value_json.${property} }}`,
@@ -197,6 +203,7 @@ export default class HomeAssistant extends Extension {
const different = ['valve_detection', 'window_detection', 'auto_lock', 'away_mode'];
if (different.includes(property)) {
discoveryEntry.discovery_payload.name = firstExpose.label;
discoveryEntry.discovery_payload.command_topic_postfix = property;
discoveryEntry.discovery_payload.state_off = state.value_off;
discoveryEntry.discovery_payload.state_on = state.value_on;
@@ -220,6 +227,7 @@ export default class HomeAssistant extends Extension {
object_id: endpoint ? `climate_${endpoint}` : 'climate',
mockProperties: [],
discovery_payload: {
name: endpoint ? utils.capitalize(endpoint) : null,
// Static
state_topic: false,
temperature_unit: 'C',
@@ -308,6 +316,7 @@ export default class HomeAssistant extends Extension {
object_id: endpoint ? `${tempCalibration.name}_${endpoint}` : `${tempCalibration.name}`,
mockProperties: [{property: tempCalibration.property, value: null}],
discovery_payload: {
name: endpoint ? `${tempCalibration.label} ${endpoint}` : tempCalibration.label,
value_template: `{{ value_json.${tempCalibration.property} }}`,
command_topic: true,
command_topic_prefix: endpoint,
@@ -334,6 +343,7 @@ export default class HomeAssistant extends Extension {
object_id: endpoint ? `${piHeatingDemand.name}_${endpoint}` : `${piHeatingDemand.name}`,
mockProperties: [{property: piHeatingDemand.property, value: null}],
discovery_payload: {
name: endpoint ? `${piHeatingDemand.label} ${endpoint}` : piHeatingDemand.label,
value_template: `{{ value_json.${piHeatingDemand.property} }}`,
...(piHeatingDemand.unit && {unit_of_measurement: piHeatingDemand.unit}),
entity_category: 'diagnostic',
@@ -354,6 +364,7 @@ export default class HomeAssistant extends Extension {
object_id: 'lock',
mockProperties: [{property: state.property, value: null}],
discovery_payload: {
name: null,
command_topic: true,
value_template: `{{ value_json.${state.property} }}`,
},
@@ -361,12 +372,14 @@ export default class HomeAssistant extends Extension {
if (state.property === 'keypad_lockout') {
// deprecated: keypad_lockout is messy, but changing is breaking
discoveryEntry.discovery_payload.name = firstExpose.label;
discoveryEntry.discovery_payload.payload_lock = state.value_on;
discoveryEntry.discovery_payload.payload_unlock = state.value_off;
discoveryEntry.discovery_payload.state_topic = true;
discoveryEntry.object_id = 'keypad_lock';
} else if (state.property === 'child_lock') {
// deprecated: child_lock is messy, but changing is breaking
discoveryEntry.discovery_payload.name = firstExpose.label;
discoveryEntry.discovery_payload.payload_lock = state.value_on;
discoveryEntry.discovery_payload.payload_unlock = state.value_off;
discoveryEntry.discovery_payload.state_locked = 'LOCK';
@@ -399,6 +412,7 @@ export default class HomeAssistant extends Extension {
mockProperties: [{property: state.property, value: null}],
object_id: endpoint ? `cover_${endpoint}` : 'cover',
discovery_payload: {
name: endpoint ? utils.capitalize(endpoint) : null,
command_topic_prefix: endpoint,
command_topic: true,
state_topic: true,
@@ -473,6 +487,7 @@ export default class HomeAssistant extends Extension {
object_id: 'fan',
mockProperties: [{property: 'fan_state', value: null}],
discovery_payload: {
name: null,
state_topic: true,
state_value_template: '{{ value_json.fan_state }}',
command_topic: true,
@@ -584,6 +599,7 @@ export default class HomeAssistant extends Extension {
`switch_${firstExpose.name}_${endpoint}` :
`switch_${firstExpose.name}`,
discovery_payload: {
name: endpoint ? `${firstExpose.label} ${endpoint}` : firstExpose.label,
value_template: typeof firstExpose.value_on === 'boolean' ?
`{% if value_json.${firstExpose.property} %} true {% else %} false {% endif %}` :
`{{ value_json.${firstExpose.property} }}`,
@@ -595,6 +611,10 @@ export default class HomeAssistant extends Extension {
...(lookup[firstExpose.name] || {}),
},
};
// Let Home Assistant generate entity name when device_class is present
if (discoveryEntry.discovery_payload.device_class) delete discoveryEntry.discovery_payload.name;
discoveryEntries.push(discoveryEntry);
} else {
const discoveryEntry: DiscoveryEntry = {
@@ -602,12 +622,17 @@ export default class HomeAssistant extends Extension {
object_id: endpoint ? `${firstExpose.name}_${endpoint}` : `${firstExpose.name}`,
mockProperties: [{property: firstExpose.property, value: null}],
discovery_payload: {
name: endpoint ? `${firstExpose.label} ${endpoint}` : firstExpose.label,
value_template: `{{ value_json.${firstExpose.property} }}`,
payload_on: firstExpose.value_on,
payload_off: firstExpose.value_off,
...(lookup[firstExpose.name] || {}),
},
};
// Let Home Assistant generate entity name when device_class is present
if (discoveryEntry.discovery_payload.device_class) delete discoveryEntry.discovery_payload.name;
discoveryEntries.push(discoveryEntry);
}
} else if (firstExpose.type === 'numeric') {
@@ -755,6 +780,7 @@ export default class HomeAssistant extends Extension {
object_id: endpoint ? `${firstExpose.name}_${endpoint}` : `${firstExpose.name}`,
mockProperties: [{property: firstExpose.property, value: null}],
discovery_payload: {
name: endpoint ? `${firstExpose.label} ${endpoint}` : firstExpose.label,
value_template: `{{ value_json.${firstExpose.property} }}`,
enabled_by_default: !allowsSet,
...(firstExpose.unit && {unit_of_measurement: firstExpose.unit}),
@@ -776,6 +802,9 @@ export default class HomeAssistant extends Extension {
delete discoveryEntry.discovery_payload.device_class;
}
// Let Home Assistant generate entity name when device_class is present
if (discoveryEntry.discovery_payload.device_class) delete discoveryEntry.discovery_payload.name;
discoveryEntries.push(discoveryEntry);
/**
@@ -789,6 +818,7 @@ export default class HomeAssistant extends Extension {
object_id: endpoint ? `${firstExpose.name}_${endpoint}` : `${firstExpose.name}`,
mockProperties: [{property: firstExpose.property, value: null}],
discovery_payload: {
name: endpoint ? `${firstExpose.label} ${endpoint}` : firstExpose.label,
value_template: `{{ value_json.${firstExpose.property} }}`,
command_topic: true,
command_topic_prefix: endpoint,
@@ -805,6 +835,9 @@ export default class HomeAssistant extends Extension {
delete discoveryEntry.discovery_payload.device_class;
}
// Let Home Assistant generate entity name when device_class is present
if (discoveryEntry.discovery_payload.device_class) delete discoveryEntry.discovery_payload.name;
if (firstExpose.value_min != null) discoveryEntry.discovery_payload.min = firstExpose.value_min;
if (firstExpose.value_max != null) discoveryEntry.discovery_payload.max = firstExpose.value_max;
@@ -856,6 +889,7 @@ export default class HomeAssistant extends Extension {
object_id: firstExpose.property,
mockProperties: [{property: firstExpose.property, value: null}],
discovery_payload: {
name: endpoint ? `${firstExpose.label} ${endpoint}` : firstExpose.label,
value_template: valueTemplate,
enabled_by_default: !(firstExpose.access & ACCESS_SET),
...lookup[firstExpose.name],
@@ -874,6 +908,7 @@ export default class HomeAssistant extends Extension {
object_id: firstExpose.property,
mockProperties: [], // Already mocked above in case access STATE is supported
discovery_payload: {
name: endpoint ? `${firstExpose.label} ${endpoint}` : firstExpose.label,
value_template: valueTemplate,
state_topic: !!(firstExpose.access & ACCESS_STATE),
command_topic_prefix: endpoint,
@@ -890,7 +925,7 @@ export default class HomeAssistant extends Extension {
const lookup: {[s: string]: KeyValue} = {
action: {icon: 'mdi:gesture-double-tap'},
programming_mode: {icon: 'mdi:calendar-clock'},
program: {value_template: `{{ value_json.${firstExpose.property}|default("") ` +
program: {value_template: `{{ value_json.${firstExpose.property}|default('',true) ` +
`| truncate(254, True, '', 0) }}`},
};
if (firstExpose.access & ACCESS_STATE) {
@@ -899,6 +934,7 @@ export default class HomeAssistant extends Extension {
object_id: firstExpose.property,
mockProperties: [{property: firstExpose.property, value: null}],
discovery_payload: {
name: endpoint ? `${firstExpose.label} ${endpoint}` : firstExpose.label,
value_template: `{{ value_json.${firstExpose.property} }}`,
enabled_by_default: !settableText,
...lookup[firstExpose.name],
@@ -912,6 +948,7 @@ export default class HomeAssistant extends Extension {
object_id: firstExpose.property,
mockProperties: [], // Already mocked above in case access STATE is supported
discovery_payload: {
name: endpoint ? `${firstExpose.label} ${endpoint}` : firstExpose.label,
state_topic: firstExpose.access & ACCESS_STATE,
value_template: `{{ value_json.${firstExpose.property} }}`,
command_topic_prefix: endpoint,
@@ -931,7 +968,7 @@ export default class HomeAssistant extends Extension {
@bind onDeviceRemoved(data: eventdata.DeviceRemoved): void {
logger.debug(`Clearing Home Assistant discovery topic for '${data.name}'`);
this.discovered[data.ieeeAddr]?.topics.forEach((topic) => {
this.mqtt.publish(topic, null, {retain: true, qos: 0}, this.discoveryTopic, false, false);
this.mqtt.publish(topic, null, {retain: true, qos: 1}, this.discoveryTopic, false, false);
});
delete this.discovered[data.ieeeAddr];
@@ -1012,7 +1049,7 @@ export default class HomeAssistant extends Extension {
if (data.homeAssisantRename) {
for (const config of this.getConfigs(data.entity)) {
const topic = this.getDiscoveryTopic(config, data.entity);
this.mqtt.publish(topic, null, {retain: true, qos: 0}, this.discoveryTopic, false, false);
this.mqtt.publish(topic, null, {retain: true, qos: 1}, this.discoveryTopic, false, false);
}
// Make sure Home Assistant deletes the old entity first otherwise another one (_2) is created
@@ -1087,6 +1124,7 @@ export default class HomeAssistant extends Extension {
object_id: 'last_seen',
mockProperties: [{property: 'last_seen', value: null}],
discovery_payload: {
name: 'Last seen',
value_template: '{{ value_json.last_seen }}',
icon: 'mdi:clock',
enabled_by_default: false,
@@ -1108,6 +1146,7 @@ export default class HomeAssistant extends Extension {
object_id: 'update_state',
mockProperties: [], // update is mocked below with updateSensor
discovery_payload: {
name: 'Update state',
icon: 'mdi:update',
value_template: `{{ value_json['update']['state'] }}`,
enabled_by_default: false,
@@ -1121,6 +1160,7 @@ export default class HomeAssistant extends Extension {
object_id: 'update_available',
mockProperties: [{property: 'update_available', value: null}],
discovery_payload: {
name: null,
payload_on: true,
payload_off: false,
value_template: `{{ value_json['update']['state'] == "available" }}`,
@@ -1135,6 +1175,7 @@ export default class HomeAssistant extends Extension {
object_id: 'update',
mockProperties: [{property: 'update', value: {state: null}}],
discovery_payload: {
name: null,
entity_picture: 'https://github.com/Koenkk/zigbee2mqtt/raw/master/images/logo.png',
latest_version_topic: true,
state_topic: true,
@@ -1223,20 +1264,20 @@ export default class HomeAssistant extends Extension {
const devicePayload = this.getDevicePayload(entity);
// Set (unique) name, separate by space if device name contains space.
const nameSeparator = devicePayload.name.includes('_') ? '_' : ' ';
payload.name = devicePayload.name;
// Suggest object_id (entity_id) for entity
payload.object_id = devicePayload.name.replace(/\s+/g, '_').toLowerCase();
if (config.object_id.startsWith(config.type) && config.object_id.includes('_')) {
payload.name += `${nameSeparator}${config.object_id.split(/_(.+)/)[1]}`;
payload.object_id += `_${config.object_id.split(/_(.+)/)[1]}`;
} else if (!config.object_id.startsWith(config.type)) {
payload.name += `${nameSeparator}${config.object_id.replace(/_/g, nameSeparator)}`;
payload.object_id += `_${config.object_id}`;
}
// Set unique_id
payload.unique_id = `${entity.options.ID}_${config.object_id}_${settings.get().mqtt.base_topic}`;
// Attributes for device registry
// Attributes for device registry and origin
payload.device = devicePayload;
payload.origin = this.discoveryOrigin;
// Availability payload
payload.availability = [{topic: `${settings.get().mqtt.base_topic}/bridge/state`}];
@@ -1381,7 +1422,7 @@ export default class HomeAssistant extends Extension {
}
const topic = this.getDiscoveryTopic(config, entity);
this.mqtt.publish(topic, stringify(payload), {retain: true, qos: 0}, this.discoveryTopic, false, false);
this.mqtt.publish(topic, stringify(payload), {retain: true, qos: 1}, this.discoveryTopic, false, false);
this.discovered[discoverKey].topics.add(topic);
this.discovered[discoverKey].objectIDs.add(config.object_id);
config.mockProperties?.forEach((mockProperty) =>
@@ -1441,7 +1482,7 @@ export default class HomeAssistant extends Extension {
if (clear) {
logger.debug(`Clearing Home Assistant config '${data.topic}'`);
const topic = data.topic.substring(this.discoveryTopic.length + 1);
this.mqtt.publish(topic, null, {retain: true, qos: 0}, this.discoveryTopic, false, false);
this.mqtt.publish(topic, null, {retain: true, qos: 1}, this.discoveryTopic, false, false);
}
} else if ((data.topic === this.statusTopic || data.topic === defaultStatusTopic) &&
data.message.toLowerCase() === 'online') {
@@ -1559,9 +1600,10 @@ export default class HomeAssistant extends Extension {
payload: value,
topic: `${settings.get().mqtt.base_topic}/${device.name}/${key}`,
device: this.getDevicePayload(device),
origin: this.discoveryOrigin,
};
await this.mqtt.publish(topic, stringify(payload), {retain: true, qos: 0}, this.discoveryTopic, false, false);
await this.mqtt.publish(topic, stringify(payload), {retain: true, qos: 1}, this.discoveryTopic, false, false);
this.discoveredTriggers[device.ieeeAddr].add(discoveredKey);
}
+1 -1
View File
@@ -13,7 +13,7 @@ import bind from 'bind-decorator';
const topicRegex = new RegExp(`^(.+?)(?:/(${utils.endpointNames.join('|')}|\\d+))?/(get|set)(?:/(.+))?`);
const propertyEndpointRegex = new RegExp(`^(.*?)_(${utils.endpointNames.join('|')})$`);
const stateValues = ['on', 'off', 'toggle', 'open', 'close', 'stop', 'lock', 'unlock'];
const sceneConverterKeys = ['scene_store', 'scene_add', 'scene_remove', 'scene_remove_all'];
const sceneConverterKeys = ['scene_store', 'scene_add', 'scene_remove', 'scene_remove_all', 'scene_rename'];
// Legacy: don't provide default converters anymore, this is required by older z2m installs not saving group members
const defaultGroupConverters = [
+7 -6
View File
@@ -1,9 +1,10 @@
import mqtt from 'mqtt';
import * as mqtt from 'mqtt';
import logger from './util/logger';
import * as settings from './util/settings';
import utils from './util/utils';
import fs from 'fs';
import bind from 'bind-decorator';
import type {QoS} from 'mqtt-packet';
export default class MQTT {
private publishedTopics: Set<string> = new Set();
@@ -26,7 +27,7 @@ export default class MQTT {
const options: mqtt.IClientOptions = {
will: {
topic: `${settings.get().mqtt.base_topic}/bridge/state`,
payload: utils.availabilityPayload('offline', settings.get()),
payload: Buffer.from(utils.availabilityPayload('offline', settings.get())),
retain: settings.get().mqtt.force_disable_retain ? false : true,
qos: 1,
},
@@ -133,11 +134,11 @@ export default class MQTT {
this.client.subscribe(topic);
}
@bind public onMessage(topic: string, message: string): void {
@bind public onMessage(topic: string, message: Buffer): void {
// Since we subscribe to zigbee2mqtt/# we also receive the message we send ourselves, skip these.
if (!this.publishedTopics.has(topic)) {
logger.debug(`Received MQTT message on '${topic}' with data '${message}'`);
this.eventBus.emitMQTTMessage({topic, message: message + ''});
logger.debug(`Received MQTT message on '${topic}' with data '${message.toString()}'`);
this.eventBus.emitMQTTMessage({topic, message: message.toString()});
}
if (this.republishRetainedTimer && topic === `${settings.get().mqtt.base_topic}/bridge/info`) {
@@ -153,7 +154,7 @@ export default class MQTT {
async publish(topic: string, payload: string, options: MQTTOptions={},
base=settings.get().mqtt.base_topic, skipLog=false, skipReceive=true,
): Promise<void> {
const defaultOptions: {qos: mqtt.QoS, retain: boolean} = {qos: 0, retain: false};
const defaultOptions: {qos: QoS, retain: boolean} = {qos: 0, retain: false};
topic = `${base}/${topic}`;
if (skipReceive) {
+6 -6
View File
@@ -31,7 +31,7 @@ import type TypeDevice from 'lib/model/device';
import type TypeGroup from 'lib/model/group';
import type TypeExtension from 'lib/extension/extension';
import type mqtt from 'mqtt';
import type {QoS} from 'mqtt-packet';
declare global {
// Define some class types as global
@@ -45,7 +45,7 @@ declare global {
// Types
interface MQTTResponse {data: KeyValue, status: 'error' | 'ok', error?: string, transaction?: string}
interface MQTTOptions {qos?: mqtt.QoS, retain?: boolean, properties?: {messageExpiryInterval: number}}
interface MQTTOptions {qos?: QoS, retain?: boolean, properties?: {messageExpiryInterval: number}}
type StateChangeReason = 'publishDebounce' | 'groupOptimistic' | 'lastSeenChanged' | 'publishCached';
type PublishEntityState = (entity: Device | Group, payload: KeyValue,
stateChangeReason?: StateChangeReason) => Promise<void>;
@@ -98,12 +98,12 @@ declare global {
meta: {state: KeyValue, logger: Logger, device: zh.Device}) => Promise<KeyValue>,
}
interface DefinitionExposeFeature {name: string, endpoint?: string,
interface DefinitionExposeFeature {name: string, label: string, endpoint?: string,
property: string, value_max?: number, value_min?: number, unit?: string,
value_off?: string, value_on?: string, value_step?: number, values: string[], access: number}
interface DefinitionExpose {
type: string, name?: string, features?: DefinitionExposeFeature[],
type: string, name?: string, label?: string, features?: DefinitionExposeFeature[],
endpoint?: string, values?: string[], value_off?: string, value_on?: string, value_step?: number,
access: number, property: string, unit?: string,
value_min?: number, value_max?: number}
@@ -189,7 +189,7 @@ declare global {
base_topic: string,
include_device_information: boolean,
force_disable_retain: boolean
version?: number,
version?: 3 | 4 | 5,
user?: string,
password?: string,
server: string,
@@ -257,7 +257,7 @@ declare global {
log_level: 'debug' | 'info' | 'error' | 'warn',
log_syslog: KeyValue,
pan_id: number | 'GENERATE',
ext_pan_id: number[],
ext_pan_id: number[] | 'GENERATE',
channel: number,
adapter_concurrent: number | null,
adapter_delay: number | null,
+12 -4
View File
@@ -582,10 +582,18 @@
"description": "ZigBee pan ID, changing requires repairing all devices!"
},
"ext_pan_id": {
"type": "array",
"items": {
"type": "number"
},
"oneOf": [{
"type": "string",
"title": "Extended pan ID (string)"
},
{
"type": "array",
"items": {
"type": "number"
},
"title": "Extended pan ID (array)"
}
],
"title": "Ext Pan ID",
"requiresRestart": true,
"description": "Zigbee extended pan ID, changing requires repairing all devices!"
+5
View File
@@ -285,6 +285,11 @@ export function validate(): string[] {
errors.push(`advanced.pan_id: should be number or 'GENERATE' (is '${_settings.advanced.pan_id}')`);
}
if (_settings.advanced && _settings.advanced.ext_pan_id && typeof _settings.advanced.ext_pan_id === 'string' &&
_settings.advanced.ext_pan_id !== 'GENERATE') {
errors.push(`advanced.ext_pan_id: should be array or 'GENERATE' (is '${_settings.advanced.ext_pan_id}')`);
}
// Verify that all friendly names are unique
const names: string[] = [];
const check = (e: DeviceOptions | GroupOptions): void => {
+13 -1
View File
@@ -28,7 +28,8 @@ export default class Zigbee {
network: {
panID: settings.get().advanced.pan_id === 'GENERATE' ?
this.generatePanID() : settings.get().advanced.pan_id as number,
extendedPanID: settings.get().advanced.ext_pan_id,
extendedPanID: settings.get().advanced.ext_pan_id === 'GENERATE' ?
this.generateExtPanID() : settings.get().advanced.ext_pan_id as number[],
channelList: [settings.get().advanced.channel],
networkKey: settings.get().advanced.network_key === 'GENERATE' ?
this.generateNetworkKey() : settings.get().advanced.network_key as number[],
@@ -170,6 +171,12 @@ export default class Zigbee {
return key;
}
private generateExtPanID(): number[] {
const key = Array.from({length: 8}, () => randomInt(256));
settings.set(['advanced', 'ext_pan_id'], key);
return key;
}
private generatePanID(): number {
const panID = randomInt(1, 0xFFFF - 1);
settings.set(['advanced', 'pan_id'], panID);
@@ -188,6 +195,11 @@ export default class Zigbee {
return this.herdsman.backup();
}
async coordinatorCheck(): Promise<{missingRouters: Device[]}> {
const check = await this.herdsman.coordinatorCheck();
return {missingRouters: check.missingRouters.map((d) => this.resolveDevice(d.ieeeAddr))};
}
async getNetworkParameters(): Promise<zh.NetworkParameters> {
return this.herdsman.getNetworkParameters();
}
+1657 -1583
View File
File diff suppressed because it is too large Load Diff
+18 -17
View File
@@ -1,6 +1,6 @@
{
"name": "zigbee2mqtt",
"version": "1.32.2",
"version": "1.33.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": "^14 || ^16 || ^18 || ^19 || ^20"
"node": "^16 || ^18 || ^19 || ^20"
},
"keywords": [
"xiaomi",
@@ -49,7 +49,7 @@
"jszip": "^3.10.1",
"mkdir-recursive": "^0.4.0",
"moment": "^2.29.4",
"mqtt": "4.3.7",
"mqtt": "5.0.3",
"object-assign-deep": "^0.4.0",
"rimraf": "^5.0.1",
"semver": "^7.5.4",
@@ -59,33 +59,34 @@
"winston-syslog": "^2.7.0",
"winston-transport": "^4.5.0",
"ws": "^8.13.0",
"zigbee-herdsman": "0.17.2",
"zigbee-herdsman-converters": "15.50.1",
"zigbee2mqtt-frontend": "0.6.133"
"zigbee-herdsman": "0.18.5",
"zigbee-herdsman-converters": "15.67.1",
"zigbee2mqtt-frontend": "0.6.135"
},
"devDependencies": {
"@babel/core": "^7.22.9",
"@babel/core": "^7.22.11",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-proposal-decorators": "^7.22.7",
"@babel/preset-env": "^7.22.9",
"@babel/preset-typescript": "^7.22.5",
"@babel/plugin-proposal-decorators": "^7.22.10",
"@babel/preset-env": "^7.22.10",
"@babel/preset-typescript": "^7.22.11",
"@types/debounce": "^1.2.1",
"@types/finalhandler": "^1.2.0",
"@types/humanize-duration": "^3.27.1",
"@types/jest": "^29.5.3",
"@types/jest": "^29.5.4",
"@types/js-yaml": "^4.0.5",
"@types/object-assign-deep": "^0.4.0",
"@types/readable-stream": "4.0.0",
"@types/rimraf": "^3.0.2",
"@types/ws": "8.5.4",
"@typescript-eslint/eslint-plugin": "^6.1.0",
"@typescript-eslint/parser": "^6.1.0",
"babel-jest": "^29.6.1",
"eslint": "^8.45.0",
"@typescript-eslint/eslint-plugin": "^6.4.1",
"@typescript-eslint/parser": "^6.4.1",
"babel-jest": "^29.6.4",
"eslint": "^8.48.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-jest": "^27.2.3",
"jest": "^29.6.1",
"jest": "^29.6.4",
"tmp": "^0.2.1",
"typescript": "^5.1.6"
"typescript": "^5.2.2"
},
"jest": {
"coverageThreshold": {
+17 -17
View File
@@ -60,11 +60,11 @@ describe('Availability', () => {
it('Should publish availabilty on startup for device where it is enabled for', async () => {
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
'online', {retain: true, qos: 0}, expect.any(Function));
'online', {retain: true, qos: 1}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote/availability',
'online', {retain: true, qos: 0}, expect.any(Function));
'online', {retain: true, qos: 1}, expect.any(Function));
expect(MQTT.publish).not.toHaveBeenCalledWith('zigbee2mqtt/bulb_color_2/availability',
'online', {retain: true, qos: 0}, expect.any(Function));
'online', {retain: true, qos: 1}, expect.any(Function));
});
it('Should publish offline for active device when not seen for 10 minutes', async () => {
@@ -77,7 +77,7 @@ describe('Availability', () => {
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1);
expect(devices.bulb_color.ping).toHaveBeenNthCalledWith(1, true);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
'offline', {retain: true, qos: 1}, expect.any(Function));
});
it('Shouldnt do anything for a device when availability: false is set for device', async () => {
@@ -93,7 +93,7 @@ describe('Availability', () => {
await advancedTime(utils.hours(26));
expect(devices.remote.ping).toHaveBeenCalledTimes(0);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
'offline', {retain: true, qos: 1}, expect.any(Function));
});
it('Should reset ping timer when device last seen changes for active device', async () => {
@@ -111,7 +111,7 @@ describe('Availability', () => {
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1);
expect(devices.bulb_color.ping).toHaveBeenNthCalledWith(1, true);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
'offline', {retain: true, qos: 1}, expect.any(Function));
});
it('Should ping again when first ping fails', async () => {
@@ -132,7 +132,7 @@ describe('Availability', () => {
expect(devices.bulb_color.ping).toHaveBeenNthCalledWith(1, true);
expect(devices.bulb_color.ping).toHaveBeenNthCalledWith(2, false);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
'offline', {retain: true, qos: 1}, expect.any(Function));
});
it('Should reset ping timer when device last seen changes for passive device', async () => {
@@ -150,7 +150,7 @@ describe('Availability', () => {
await advancedTime(utils.hours(3));
expect(devices.remote.ping).toHaveBeenCalledTimes(0);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
'offline', {retain: true, qos: 1}, expect.any(Function));
});
it('Should immediately mark device as online when it lastSeen changes', async () => {
@@ -158,13 +158,13 @@ describe('Availability', () => {
await advancedTime(utils.minutes(15));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
'offline', {retain: true, qos: 1}, expect.any(Function));
devices.bulb_color.lastSeen = Date.now();
await zigbeeHerdsman.events.lastSeenChanged({device: devices.bulb_color});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
'online', {retain: true, qos: 0}, expect.any(Function));
'online', {retain: true, qos: 1}, expect.any(Function));
});
it('Should allow to change availability timeout via device options', async () => {
@@ -283,12 +283,12 @@ describe('Availability', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
null, {retain: true, qos: 0}, expect.any(Function));
null, {retain: true, qos: 1}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_new_name/availability',
'online', {retain: true, qos: 0}, expect.any(Function));
'online', {retain: true, qos: 1}, expect.any(Function));
await advancedTime(utils.hours(12));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_new_name/availability',
'offline', {retain: true, qos: 0}, expect.any(Function));
'offline', {retain: true, qos: 1}, expect.any(Function));
});
it('Should publish availabiltiy payload in JSON format', async () => {
@@ -298,7 +298,7 @@ describe('Availability', () => {
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));
stringify({state: 'offline'}), {retain: true, qos: 1}, expect.any(Function));
});
it('Deprecated - should allow to block via advanced.availability_blocklist', async () => {
@@ -332,16 +332,16 @@ describe('Availability', () => {
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));
'online', {retain: true, qos: 1}, 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));
'offline', {retain: true, qos: 1}, 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));
'online', {retain: true, qos: 1}, expect.any(Function));
});
});
+14 -2
View File
File diff suppressed because one or more lines are too long
+7 -4
View File
@@ -52,7 +52,7 @@ describe('Controller', () => {
expect(logger.info).toHaveBeenCalledWith('remote (0x0017880104e45517): 324131092621 - Philips Hue dimmer switch (EndDevice)');
expect(logger.info).toHaveBeenCalledWith('0x0017880104e45518 (0x0017880104e45518): Not supported (EndDevice)');
expect(MQTT.connect).toHaveBeenCalledTimes(1);
expect(MQTT.connect).toHaveBeenCalledWith("mqtt://localhost", {"will": {"payload": "offline", "retain": true, "topic": "zigbee2mqtt/bridge/state", "qos": 1}});
expect(MQTT.connect).toHaveBeenCalledWith("mqtt://localhost", {"will": {"payload": Buffer.from("offline"), "retain": true, "topic": "zigbee2mqtt/bridge/state", "qos": 1}});
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99}),{ retain: true, qos: 0 }, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote', stringify({"brightness":255}), { retain: true, qos: 0 }, expect.any(Function));
});
@@ -88,7 +88,7 @@ describe('Controller', () => {
await flushPromises();
expect(MQTT.connect).toHaveBeenCalledTimes(1);
const expected = {
"will": {"payload": "offline", "retain": true, "topic": "zigbee2mqtt/bridge/state", "qos": 1},
"will": {"payload": Buffer.from("offline"), "retain": true, "topic": "zigbee2mqtt/bridge/state", "qos": 1},
keepalive: 30,
ca: Buffer.from([99, 97]),
key: Buffer.from([107, 101, 121]),
@@ -103,14 +103,17 @@ describe('Controller', () => {
expect(MQTT.connect).toHaveBeenCalledWith("mqtt://localhost", expected);
});
it('Should generate network_key and pan_id when set to GENERATE', async () => {
it('Should generate network_key, pan_id and ext_pan_id when set to GENERATE', async () => {
settings.set(['advanced', 'network_key'], 'GENERATE');
settings.set(['advanced', 'pan_id'], 'GENERATE');
settings.set(['advanced', 'ext_pan_id'], 'GENERATE');
await controller.start();
await flushPromises();
expect(zigbeeHerdsman.constructor.mock.calls[0][0].network.networkKey.length).toStrictEqual(16);
expect(zigbeeHerdsman.constructor.mock.calls[0][0].network.extendedPanID.length).toStrictEqual(8);
expect(zigbeeHerdsman.constructor.mock.calls[0][0].network.panID).toStrictEqual(expect.any(Number));
expect(data.read().advanced.network_key.length).toStrictEqual(16);
expect(data.read().advanced.ext_pan_id.length).toStrictEqual(8);
expect(data.read().advanced.pan_id).toStrictEqual(expect.any(Number));
});
@@ -640,7 +643,7 @@ describe('Controller', () => {
await flushPromises();
expect(MQTT.connect).toHaveBeenCalledTimes(1);
const expected = {
"will": { "payload": "offline", "retain": false, "topic": "zigbee2mqtt/bridge/state", "qos": 1 },
"will": { "payload": Buffer.from("offline"), "retain": false, "topic": "zigbee2mqtt/bridge/state", "qos": 1 },
}
expect(MQTT.connect).toHaveBeenCalledWith("mqtt://localhost", expected);
});
+1 -1
View File
@@ -197,7 +197,7 @@ describe('Frontend', () => {
});
it('onlythis Websocket interaction', async () => {
it('Websocket interaction', async () => {
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
+156 -100
View File
@@ -13,6 +13,7 @@ describe('HomeAssistant extension', () => {
let version;
let controller;
let extension;
let origin;
let resetExtension = async () => {
await controller.enableDisableExtension(false, 'HomeAssistant');
@@ -32,6 +33,7 @@ describe('HomeAssistant extension', () => {
beforeAll(async () => {
version = await require('../lib/util/utils').default.getZigbee2MQTTVersion();
origin = {name: 'Zigbee2MQTT', sw: version.version, url: 'https://www.zigbee2mqtt.io'}
version = `Zigbee2MQTT ${version.version}`;
jest.useFakeTimers();
settings.set(['homeassistant'], true);
@@ -58,10 +60,8 @@ describe('HomeAssistant extension', () => {
configs.forEach((c) => {
const id = c['type'] + '/' + c['object_id'];
if (cfg_type_object_ids.includes(id)) {
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 {
// A dynamic function must exposes all possible attributes for the docs
if (typeof d.exposes != 'function') {
duplicated.push(d.model);
}
} else {
@@ -91,7 +91,7 @@ describe('HomeAssistant extension', () => {
"max_mireds": 454,
"min_mireds": 250,
"json_attributes_topic":"zigbee2mqtt/ha_discovery_group",
"name":"ha_discovery_group",
"name":null,
"schema":"json",
"state_topic":"zigbee2mqtt/ha_discovery_group",
"supported_color_modes":[
@@ -111,13 +111,15 @@ describe('HomeAssistant extension', () => {
"stop_effect",
"stop_hue_effect",
],
"unique_id":"9_light_zigbee2mqtt"
"object_id":"ha_discovery_group",
"unique_id":"9_light_zigbee2mqtt",
'origin': origin,
};
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/light/1221051039810110150109113116116_9/light/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -130,18 +132,20 @@ describe('HomeAssistant extension', () => {
"sw_version": version,
},
"json_attributes_topic":"zigbee2mqtt/ha_discovery_group",
"name":"ha_discovery_group",
"name":null,
"payload_off":"OFF",
"payload_on":"ON",
"state_topic":"zigbee2mqtt/ha_discovery_group",
"object_id":"ha_discovery_group",
"unique_id":"9_switch_zigbee2mqtt",
'origin': origin,
"value_template":"{{ value_json.state }}"
};
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/switch/1221051039810110150109113116116_9/switch/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -152,8 +156,9 @@ describe('HomeAssistant extension', () => {
'value_template': '{{ value_json.temperature }}',
'state_topic': 'zigbee2mqtt/weather_sensor',
'json_attributes_topic': 'zigbee2mqtt/weather_sensor',
'name': 'weather_sensor_temperature',
'object_id': 'weather_sensor_temperature',
'unique_id': '0x0017880104e45522_temperature_zigbee2mqtt',
'origin': origin,
'device': {
'identifiers': ['zigbee2mqtt_0x0017880104e45522'],
'name': 'weather_sensor',
@@ -168,7 +173,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/temperature/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -179,8 +184,9 @@ describe('HomeAssistant extension', () => {
'value_template': '{{ value_json.humidity }}',
'state_topic': 'zigbee2mqtt/weather_sensor',
'json_attributes_topic': 'zigbee2mqtt/weather_sensor',
'name': 'weather_sensor_humidity',
'object_id': 'weather_sensor_humidity',
'unique_id': '0x0017880104e45522_humidity_zigbee2mqtt',
'origin': origin,
'enabled_by_default': true,
'device': {
'identifiers': ['zigbee2mqtt_0x0017880104e45522'],
@@ -195,7 +201,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/humidity/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -206,8 +212,9 @@ describe('HomeAssistant extension', () => {
'value_template': '{{ value_json.pressure }}',
'state_topic': 'zigbee2mqtt/weather_sensor',
'json_attributes_topic': 'zigbee2mqtt/weather_sensor',
'name': 'weather_sensor_pressure',
'object_id': 'weather_sensor_pressure',
'unique_id': '0x0017880104e45522_pressure_zigbee2mqtt',
'origin': origin,
'enabled_by_default': true,
'device': {
'identifiers': ['zigbee2mqtt_0x0017880104e45522'],
@@ -222,7 +229,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/pressure/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -233,8 +240,9 @@ describe('HomeAssistant extension', () => {
'value_template': '{{ value_json.battery }}',
'state_topic': 'zigbee2mqtt/weather_sensor',
'json_attributes_topic': 'zigbee2mqtt/weather_sensor',
'name': 'weather_sensor_battery',
'object_id': 'weather_sensor_battery',
'unique_id': '0x0017880104e45522_battery_zigbee2mqtt',
'origin': origin,
'enabled_by_default': true,
'entity_category': 'diagnostic',
'device': {
@@ -250,7 +258,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/battery/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -263,8 +271,10 @@ describe('HomeAssistant extension', () => {
'value_template': '{{ value_json.linkquality }}',
'state_topic': 'zigbee2mqtt/weather_sensor',
'json_attributes_topic': 'zigbee2mqtt/weather_sensor',
'name': 'weather_sensor_linkquality',
'name': 'Linkquality',
'object_id': 'weather_sensor_linkquality',
'unique_id': '0x0017880104e45522_linkquality_zigbee2mqtt',
'origin': origin,
'device': {
'identifiers': ['zigbee2mqtt_0x0017880104e45522'],
'name': 'weather_sensor',
@@ -278,7 +288,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/linkquality/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -295,18 +305,20 @@ describe('HomeAssistant extension', () => {
"sw_version": null
},
"json_attributes_topic":"zigbee2mqtt/wall_switch_double",
"name":"wall_switch_double_left",
"name":"Left",
"payload_off":"OFF",
"payload_on":"ON",
"state_topic":"zigbee2mqtt/wall_switch_double",
"object_id":"wall_switch_double_left",
"unique_id":"0x0017880104e45542_switch_left_zigbee2mqtt",
'origin': origin,
"value_template":"{{ value_json.state_left }}"
};
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/switch/0x0017880104e45542/switch_left/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -323,18 +335,20 @@ describe('HomeAssistant extension', () => {
"sw_version": null
},
"json_attributes_topic":"zigbee2mqtt/wall_switch_double",
"name":"wall_switch_double_right",
"name":"Right",
"payload_off":"OFF",
"payload_on":"ON",
"state_topic":"zigbee2mqtt/wall_switch_double",
"object_id":"wall_switch_double_right",
"unique_id":"0x0017880104e45542_switch_right_zigbee2mqtt",
'origin': origin,
"value_template":"{{ value_json.state_right }}"
};
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/switch/0x0017880104e45542/switch_right/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -366,16 +380,18 @@ describe('HomeAssistant extension', () => {
"stop_effect"
],
"json_attributes_topic":"zigbee2mqtt/bulb",
"name":"bulb",
"name":null,
"schema":"json",
"state_topic":"zigbee2mqtt/bulb",
"object_id":"bulb",
"unique_id":"0x000b57fffec6a5b2_light_zigbee2mqtt",
'origin': origin,
};
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/light/0x000b57fffec6a5b2/light/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -402,8 +418,9 @@ describe('HomeAssistant extension', () => {
'value_template': "{{ value_json.temperature }}",
'state_topic': 'zigbee2mqtt/weather_sensor',
'json_attributes_topic': 'zigbee2mqtt/weather_sensor',
'name': 'weather_sensor_temperature',
'object_id': 'weather_sensor_temperature',
'unique_id': '0x0017880104e45522_temperature_zigbee2mqtt',
'origin': origin,
'device': {
'identifiers': ['zigbee2mqtt_0x0017880104e45522'],
'name': 'weather_sensor',
@@ -417,7 +434,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/temperature/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -428,8 +445,9 @@ describe('HomeAssistant extension', () => {
'value_template': '{{ value_json.humidity }}',
'state_topic': 'zigbee2mqtt/weather_sensor',
'json_attributes_topic': 'zigbee2mqtt/weather_sensor',
'name': 'weather_sensor_humidity',
'object_id': 'weather_sensor_humidity',
'unique_id': '0x0017880104e45522_humidity_zigbee2mqtt',
'origin': origin,
'enabled_by_default': true,
'device': {
'identifiers': ['zigbee2mqtt_0x0017880104e45522'],
@@ -444,7 +462,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/humidity/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -455,9 +473,10 @@ describe('HomeAssistant extension', () => {
'value_template': '{{ value_json.pressure }}',
'state_topic': 'zigbee2mqtt/weather_sensor',
'json_attributes_topic': 'zigbee2mqtt/weather_sensor',
'name': 'weather_sensor_pressure',
'enabled_by_default': true,
'object_id': 'weather_sensor_pressure',
'unique_id': '0x0017880104e45522_pressure_zigbee2mqtt',
'origin': origin,
'device': {
'identifiers': ['zigbee2mqtt_0x0017880104e45522'],
'name': 'weather_sensor',
@@ -471,7 +490,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/pressure/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -512,9 +531,10 @@ describe('HomeAssistant extension', () => {
'value_template': '{{ value_json.temperature }}',
'state_topic': 'zigbee2mqtt/weather_sensor',
'json_attributes_topic': 'zigbee2mqtt/weather_sensor',
'name': 'weather_sensor_temperature',
'enabled_by_default': true,
'object_id': 'weather_sensor_temperature',
'unique_id': '0x0017880104e45522_temperature_zigbee2mqtt',
'origin': origin,
'device': {
'identifiers': ['zigbee2mqtt_0x0017880104e45522'],
'name': 'weather_sensor',
@@ -530,7 +550,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/temperature/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -541,7 +561,6 @@ describe('HomeAssistant extension', () => {
'value_template': '{{ value_json.humidity }}',
'state_topic': 'zigbee2mqtt/weather_sensor',
'json_attributes_topic': 'zigbee2mqtt/weather_sensor',
'name': 'weather_sensor_humidity',
'enabled_by_default': true,
'device': {
'identifiers': ['zigbee2mqtt_0x0017880104e45522'],
@@ -550,15 +569,17 @@ describe('HomeAssistant extension', () => {
'model': 'custom model',
'manufacturer': 'Not from Xiaomi',
},
'origin': origin,
'availability': [{topic: 'zigbee2mqtt/bridge/state'}],
'expire_after': 30,
'icon': 'mdi:test',
'object_id': 'weather_sensor_humidity'
};
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/humidity/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -584,8 +605,9 @@ describe('HomeAssistant extension', () => {
'value_template': '{{ value_json.temperature }}',
'state_topic': 'zigbee2mqtt/weather_sensor',
'json_attributes_topic': 'zigbee2mqtt/weather_sensor',
'name': 'Weather Sensor temperature',
'object_id': 'weather_sensor_temperature',
'unique_id': '0x0017880104e45522_temperature_zigbee2mqtt',
'origin': origin,
'device': {
'identifiers': ['zigbee2mqtt_0x0017880104e45522'],
'name': 'Weather Sensor',
@@ -600,7 +622,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/temperature/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -611,8 +633,9 @@ describe('HomeAssistant extension', () => {
'value_template': '{{ value_json.humidity }}',
'state_topic': 'zigbee2mqtt/weather_sensor',
'json_attributes_topic': 'zigbee2mqtt/weather_sensor',
'name': 'Weather Sensor humidity',
'object_id': 'weather_sensor_humidity',
'unique_id': '0x0017880104e45522_humidity_zigbee2mqtt',
'origin': origin,
'enabled_by_default': true,
'device': {
'identifiers': ['zigbee2mqtt_0x0017880104e45522'],
@@ -627,7 +650,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/humidity/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -669,14 +692,16 @@ describe('HomeAssistant extension', () => {
"payload_off": "OFF",
"payload_on": "ON",
"state_topic": "zigbee2mqtt/my_switch",
"object_id": "my_switch",
"unique_id": "0x0017880104e45541_light_zigbee2mqtt",
'origin': origin,
"value_template": "{{ value_json.state }}"
}
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/light/0x0017880104e45541/light/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -732,8 +757,10 @@ describe('HomeAssistant extension', () => {
"speed_range_min":1,
"speed_range_max":4,
"json_attributes_topic":"zigbee2mqtt/fan",
"name":"fan",
"name":null,
"object_id": "fan",
"unique_id":"0x0017880104e45548_fan_zigbee2mqtt",
'origin': origin,
"device":{
"identifiers":[
"zigbee2mqtt_0x0017880104e45548"
@@ -749,7 +776,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/fan/0x0017880104e45548/fan/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -799,19 +826,21 @@ describe('HomeAssistant extension', () => {
"auto",
"off"
],
"name":"TS0601_thermostat",
"name":null,
"temp_step":0.5,
"temperature_command_topic":"zigbee2mqtt/TS0601_thermostat/set/current_heating_setpoint",
"temperature_state_template":"{{ value_json.current_heating_setpoint }}",
"temperature_state_topic":"zigbee2mqtt/TS0601_thermostat",
"temperature_unit":"C",
"unique_id":"0x0017882104a44559_climate_zigbee2mqtt"
"object_id": "ts0601_thermostat",
"unique_id":"0x0017882104a44559_climate_zigbee2mqtt",
'origin': origin,
};
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/climate/0x0017882104a44559/climate/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -831,8 +860,10 @@ describe('HomeAssistant extension', () => {
state_closed: 'CLOSE',
state_stopped: 'STOP',
json_attributes_topic: 'zigbee2mqtt/smart vent',
name: 'smart vent',
name: null,
object_id: 'smart_vent',
unique_id: '0x0017880104e45551_cover_zigbee2mqtt',
'origin': origin,
device:
{
identifiers: [ 'zigbee2mqtt_0x0017880104e45551' ],
@@ -847,7 +878,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/cover/0x0017880104e45551/cover/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -862,7 +893,7 @@ describe('HomeAssistant extension', () => {
"sw_version": null
},
"json_attributes_topic": "zigbee2mqtt/zigfred_plus/l6",
"name": "zigfred_plus_l6",
"name": "L6",
"position_template": "{{ value_json.position }}",
"position_topic": "zigbee2mqtt/zigfred_plus/l6",
"set_position_template": "{ \"position_l6\": {{ position }} }",
@@ -874,14 +905,16 @@ describe('HomeAssistant extension', () => {
"tilt_command_topic": "zigbee2mqtt/zigfred_plus/l6/set/tilt",
"tilt_status_template": "{{ value_json.tilt }}",
"tilt_status_topic": "zigbee2mqtt/zigfred_plus/l6",
"object_id": "zigfred_plus_l6",
"unique_id": "0xf4ce368a38be56a1_cover_l6_zigbee2mqtt",
'origin': origin,
"value_template": "{{ value_json.state }}"
}
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/cover/0xf4ce368a38be56a1/cover_l6/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -900,8 +933,9 @@ describe('HomeAssistant extension', () => {
'state_topic': 'zigbee2mqtt/weather_sensor',
'json_attributes_topic': 'zigbee2mqtt/weather_sensor',
'enabled_by_default': true,
'name': 'weather_sensor_temperature',
'object_id': 'weather_sensor_temperature',
'unique_id': '0x0017880104e45522_temperature_zigbee2mqtt',
'origin': origin,
'device': {
'identifiers': ['zigbee2mqtt_0x0017880104e45522'],
'name': 'weather_sensor',
@@ -915,7 +949,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'my_custom_discovery_topic/sensor/0x0017880104e45522/temperature/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -1027,8 +1061,9 @@ describe('HomeAssistant extension', () => {
'value_template': '{{ value_json.temperature }}',
'state_topic': 'zigbee2mqtt/weather_sensor',
'json_attributes_topic': 'zigbee2mqtt/weather_sensor',
'name': 'weather_sensor_temperature',
'object_id': 'weather_sensor_temperature',
'unique_id': '0x0017880104e45522_temperature_zigbee2mqtt',
'origin': origin,
'device': {
'identifiers': ['zigbee2mqtt_0x0017880104e45522'],
'name': 'weather_sensor',
@@ -1042,7 +1077,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/temperature/config',
stringify(payloadHA),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -1163,8 +1198,9 @@ describe('HomeAssistant extension', () => {
'value_template': '{{ value_json.temperature }}',
'state_topic': 'zigbee2mqtt/weather_sensor',
'json_attributes_topic': 'zigbee2mqtt/weather_sensor',
'name': 'weather_sensor_temperature',
'object_id': 'weather_sensor_temperature',
'unique_id': '0x0017880104e45522_temperature_zigbee2mqtt',
'origin': origin,
'device': {
'identifiers': ['zigbee2mqtt_0x0017880104e45522'],
'name': 'weather_sensor',
@@ -1179,7 +1215,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/temperature/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -1192,31 +1228,31 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/temperature/config',
null,
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/humidity/config',
null,
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/pressure/config',
null,
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/battery/config',
null,
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/linkquality/config',
null,
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -1245,7 +1281,8 @@ describe('HomeAssistant extension', () => {
'value_template': '{{ value_json.temperature }}',
'state_topic': 'zigbee2mqtt/weather_sensor_renamed',
'json_attributes_topic': 'zigbee2mqtt/weather_sensor_renamed',
'name': 'weather_sensor_renamed_temperature',
'object_id': 'weather_sensor_renamed_temperature',
'origin': origin,
'unique_id': '0x0017880104e45522_temperature_zigbee2mqtt',
'device': {
'identifiers': ['zigbee2mqtt_0x0017880104e45522'],
@@ -1260,14 +1297,14 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/temperature/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/temperature/config',
null,
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -1279,6 +1316,7 @@ describe('HomeAssistant extension', () => {
"subtype":"double",
"payload":"double",
"topic":"zigbee2mqtt/weather_sensor_renamed/action",
'origin': origin,
"device":{
"identifiers":[
"zigbee2mqtt_0x0017880104e45522"
@@ -1289,7 +1327,7 @@ describe('HomeAssistant extension', () => {
"manufacturer":"Xiaomi"
}
}),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -1315,7 +1353,7 @@ describe('HomeAssistant extension', () => {
"json_attributes_topic":"zigbee2mqtt/ha_discovery_group_new",
"max_mireds": 454,
"min_mireds": 250,
"name":"ha_discovery_group_new",
"name":null,
"schema":"json",
"state_topic":"zigbee2mqtt/ha_discovery_group_new",
"supported_color_modes":[
@@ -1335,20 +1373,22 @@ describe('HomeAssistant extension', () => {
"stop_effect",
"stop_hue_effect",
],
"unique_id":"9_light_zigbee2mqtt"
"object_id":"ha_discovery_group_new",
"unique_id":"9_light_zigbee2mqtt",
'origin': origin,
};
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/light/1221051039810110150109113116116_9/light/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/light/1221051039810110150109113116116_9/light/config',
null,
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -1361,7 +1401,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).not.toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/temperature/config',
null,
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -1373,8 +1413,9 @@ describe('HomeAssistant extension', () => {
'value_template': '{{ value_json.temperature }}',
'state_topic': 'zigbee2mqtt/weather_sensor_renamed',
'json_attributes_topic': 'zigbee2mqtt/weather_sensor_renamed',
'name': 'weather_sensor_renamed_temperature',
'object_id': 'weather_sensor_renamed_temperature',
'unique_id': '0x0017880104e45522_temperature_zigbee2mqtt',
'origin': origin,
'device': {
'identifiers': ['zigbee2mqtt_0x0017880104e45522'],
'name': 'weather_sensor_renamed',
@@ -1388,7 +1429,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/temperature/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -1401,8 +1442,10 @@ describe('HomeAssistant extension', () => {
"enabled_by_default": false,
"state_topic":"zigbee2mqtt/bulb",
"json_attributes_topic":"zigbee2mqtt/bulb",
"name":"bulb update available",
"name":null,
"object_id":"bulb_update_available",
"unique_id":"0x000b57fffec6a5b2_update_available_zigbee2mqtt",
'origin': origin,
"device":{
"identifiers":[
"zigbee2mqtt_0x000b57fffec6a5b2"
@@ -1420,7 +1463,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/binary_sensor/0x000b57fffec6a5b2/update_available/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -1444,6 +1487,7 @@ describe('HomeAssistant extension', () => {
"subtype":"single",
"payload":"single",
"topic":"zigbee2mqtt/button/action",
"origin": origin,
"device":{
"identifiers":[
"zigbee2mqtt_0x0017880104e45520"
@@ -1458,7 +1502,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/device_automation/0x0017880104e45520/action_single/config',
stringify(discoverPayloadAction),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -1468,6 +1512,7 @@ describe('HomeAssistant extension', () => {
"subtype":"single",
"payload":"single",
"topic":"zigbee2mqtt/button/click",
"origin": origin,
"device":{
"identifiers":[
"zigbee2mqtt_0x0017880104e45520"
@@ -1482,7 +1527,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/device_automation/0x0017880104e45520/click_single/config',
stringify(discoverPayloadClick),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -1528,14 +1573,14 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).not.toHaveBeenCalledWith(
'homeassistant/device_automation/0x0017880104e45520/action_single/config',
stringify(discoverPayloadAction),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
expect(MQTT.publish).not.toHaveBeenCalledWith(
'homeassistant/device_automation/0x0017880104e45520/click_single/config',
stringify(discoverPayloadClick),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -1632,6 +1677,7 @@ describe('HomeAssistant extension', () => {
"subtype":"single",
"payload":"single",
"topic":"zigbee2mqtt/button/action",
"origin": origin,
"device":{
"identifiers":[
"zigbee2mqtt_0x0017880104e45520"
@@ -1646,7 +1692,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/device_automation/0x0017880104e45520/action_single/config',
stringify(discoverPayload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
@@ -1711,7 +1757,7 @@ describe('HomeAssistant extension', () => {
await MQTT.events.message('homeassistant/light/1221051039810110150109113116116_91231/light/config', stringify({availability: [{topic: 'zigbee2mqtt/bridge/state'}]}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith('homeassistant/light/1221051039810110150109113116116_91231/light/config', null, {qos: 0, retain: true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('homeassistant/light/1221051039810110150109113116116_91231/light/config', null, {qos: 1, retain: true}, expect.any(Function));
// Existing group -> dont clear
MQTT.publish.mockClear();
@@ -1724,21 +1770,21 @@ describe('HomeAssistant extension', () => {
await MQTT.events.message('homeassistant/light/9/light/config', stringify({availability: [{topic: 'zigbee2mqtt/bridge/state'}]}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith('homeassistant/light/9/light/config', null, {qos: 0, retain: true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('homeassistant/light/9/light/config', null, {qos: 1, retain: true}, expect.any(Function));
// Existing group, non existing config -> clear
MQTT.publish.mockClear();
await MQTT.events.message('homeassistant/light/1221051039810110150109113116116_9/switch/config', stringify({availability: [{topic: 'zigbee2mqtt/bridge/state'}]}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith('homeassistant/light/1221051039810110150109113116116_9/switch/config', null, {qos: 0, retain: true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('homeassistant/light/1221051039810110150109113116116_9/switch/config', null, {qos: 1, retain: true}, expect.any(Function));
// Non-existing device -> clear
MQTT.publish.mockClear();
await MQTT.events.message('homeassistant/sensor/0x123/temperature/config', stringify({availability: [{topic: 'zigbee2mqtt/bridge/state'}]}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith('homeassistant/sensor/0x123/temperature/config', null, {qos: 0, retain: true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('homeassistant/sensor/0x123/temperature/config', null, {qos: 1, retain: true}, expect.any(Function));
// Existing device -> don't clear
MQTT.publish.mockClear();
@@ -1757,7 +1803,7 @@ describe('HomeAssistant extension', () => {
await MQTT.events.message('homeassistant/sensor/0x000b57fffec6a5b2/update_available/config', stringify({availability: [{topic: 'zigbee2mqtt/bridge/state'}]}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith('homeassistant/sensor/0x000b57fffec6a5b2/update_available/config', null, {qos: 0, retain: true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('homeassistant/sensor/0x000b57fffec6a5b2/update_available/config', null, {qos: 1, retain: true}, expect.any(Function));
// Non-existing device but invalid payload -> clear
MQTT.publish.mockClear();
@@ -1784,11 +1830,11 @@ describe('HomeAssistant extension', () => {
await MQTT.events.message('homeassistant/sensor/0x000b57fffec6a5b2/update_available/config', stringify({availability: [{topic: 'zigbee2mqtt/bridge/state'}]}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('homeassistant/sensor/0x000b57fffec6a5b2/update_available/config', null, {qos: 0, retain: true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('homeassistant/sensor/0x000b57fffec6a5b2/update_available/config', null, {qos: 1, retain: true}, expect.any(Function));
MQTT.publish.mockClear();
await MQTT.events.message('homeassistant/device_automation/0x000b57fffec6a5b2/action_button_3_single/config', stringify({topic: 'zigbee2mqtt/0x000b57fffec6a5b2/availability'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('homeassistant/device_automation/0x000b57fffec6a5b2/action_button_3_single/config', null, {qos: 0, retain: true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('homeassistant/device_automation/0x000b57fffec6a5b2/action_button_3_single/config', null, {qos: 1, retain: true}, expect.any(Function));
});
it('Should not have Home Assistant legacy entity attributes when disabled', async () => {
@@ -1804,8 +1850,9 @@ describe('HomeAssistant extension', () => {
'state_class': 'measurement',
'value_template': '{{ value_json.temperature }}',
'state_topic': 'zigbee2mqtt/weather_sensor',
'name': 'weather_sensor_temperature',
'object_id': 'weather_sensor_temperature',
'unique_id': '0x0017880104e45522_temperature_zigbee2mqtt',
'origin': origin,
'enabled_by_default': true,
'device': {
'identifiers': ['zigbee2mqtt_0x0017880104e45522'],
@@ -1820,7 +1867,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/temperature/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -1844,7 +1891,7 @@ describe('HomeAssistant extension', () => {
"json_attributes_topic":"zigbee2mqtt/ha_discovery_group",
"max_mireds": 454,
"min_mireds": 250,
"name":"ha_discovery_group",
"name":null,
"schema":"json",
"state_topic":"zigbee2mqtt/ha_discovery_group",
"supported_color_modes":[
@@ -1864,13 +1911,15 @@ describe('HomeAssistant extension', () => {
"stop_effect",
"stop_hue_effect",
],
"unique_id":"9_light_zigbee2mqtt"
"object_id":"ha_discovery_group",
"unique_id":"9_light_zigbee2mqtt",
'origin': origin,
};
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/light/1221051039810110150109113116116_9/light/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -1893,7 +1942,7 @@ describe('HomeAssistant extension', () => {
"max_mireds": 454,
"min_mireds": 250,
"json_attributes_topic":"zigbee2mqtt/ha_discovery_group",
"name":"ha_discovery_group",
"name":null,
"schema":"json",
"state_topic":"zigbee2mqtt/ha_discovery_group",
"supported_color_modes":[
@@ -1913,13 +1962,15 @@ describe('HomeAssistant extension', () => {
"stop_effect",
"stop_hue_effect",
],
"unique_id":"9_light_zigbee2mqtt"
"object_id":"ha_discovery_group",
"unique_id":"9_light_zigbee2mqtt",
'origin': origin,
};
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/light/1221051039810110150109113116116_9/light/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -1961,19 +2012,21 @@ describe('HomeAssistant extension', () => {
"json_attributes_topic":"zigbee2mqtt/bulb",
"max_mireds":454,
"min_mireds":250,
"name":"bulb",
"name":null,
"schema":"json",
"state_topic":"zigbee2mqtt/bulb",
"supported_color_modes":[
"color_temp"
],
"unique_id":"0x000b57fffec6a5b2_light_zigbee2mqtt"
"object_id":"bulb",
"unique_id":"0x000b57fffec6a5b2_light_zigbee2mqtt",
'origin': origin,
};
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/light/0x000b57fffec6a5b2/light/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -2000,9 +2053,11 @@ describe('HomeAssistant extension', () => {
"enabled_by_default":false,
"icon":"mdi:clock",
"json_attributes_topic":"zigbee2mqtt/bulb",
"name":"bulb last seen",
"name":"Last seen",
"state_topic":"zigbee2mqtt/bulb",
"object_id": "bulb_last_seen",
"unique_id":"0x000b57fffec6a5b2_last_seen_zigbee2mqtt",
'origin': origin,
"value_template":"{{ value_json.last_seen }}",
"device_class": "timestamp",
"entity_category": "diagnostic"
@@ -2011,7 +2066,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x000b57fffec6a5b2/last_seen/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
@@ -2032,8 +2087,9 @@ describe('HomeAssistant extension', () => {
'value_template': "{{ value_json.temperature }}",
'state_topic': 'zigbee2mqtt/weather_sensor',
'json_attributes_topic': 'zigbee2mqtt/weather_sensor',
'name': 'weather_sensor_temperature',
'object_id': 'weather_sensor_temperature',
'unique_id': '0x0017880104e45522_temperature_zigbee2mqtt',
'origin': origin,
'device': {
'identifiers': ['zigbee2mqtt_0x0017880104e45522'],
'name': 'weather_sensor',
@@ -2048,7 +2104,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/temperature/config',
stringify(payload),
{ retain: true, qos: 0 },
{ retain: true, qos: 1 },
expect.any(Function),
);
});
-5
View File
@@ -71,12 +71,7 @@ describe('Publish', () => {
});
it('Should corretly handle mallformed messages', async () => {
await MQTT.events.message('zigbee2mqtt/foo', undefined);
await MQTT.events.message('zigbee2mqtt/foo', null);
await MQTT.events.message('zigbee2mqtt/foo', "");
await MQTT.events.message('zigbee2mqtt/bulb_color/set', undefined);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', null);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', "");
await flushPromises();
expectNothingPublished();
+12
View File
@@ -682,6 +682,18 @@ describe('Settings', () => {
}).toThrow(new Error("Device '0x123' already exists"));
});
it('Should not allow any string values for ext_pan_id', () => {
write(configurationFile, {
...minimalConfig,
advanced: {ext_pan_id: 'NOT_GENERATE'},
});
settings.reRead();
const error = `advanced.ext_pan_id: should be array or 'GENERATE' (is 'NOT_GENERATE')`;
expect(settings.validate()).toEqual(expect.arrayContaining([error]));
});
it('Should not allow any string values for network_key', () => {
write(configurationFile, {
...minimalConfig,
+1
View File
@@ -214,6 +214,7 @@ const mock = {
touchlinkIdentify: jest.fn(),
start: jest.fn(),
backup: jest.fn(),
coordinatorCheck: jest.fn(),
isStopping: jest.fn(),
permitJoin: jest.fn(),
addInstallCode: jest.fn(),