mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2026-08-29 23:28:34 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46f34c8cc7 | ||
|
|
e03013c581 | ||
|
|
8a288a9119 | ||
|
|
8e27ec24f6 | ||
|
|
4b291bf012 | ||
|
|
10e6ad0e67 | ||
|
|
1b089ef6ae | ||
|
|
d7e2d98a5a | ||
|
|
eb69319674 | ||
|
|
8c8a6a3ecf | ||
|
|
baa1d703d4 | ||
|
|
37551265a2 | ||
|
|
245afb1126 | ||
|
|
389779d4e3 | ||
|
|
df23240683 | ||
|
|
75f9743528 | ||
|
|
e5d632bc91 | ||
|
|
4f97a7ad2c | ||
|
|
4bf2ffa7dd | ||
|
|
a8f6bec673 | ||
|
|
80504b943d | ||
|
|
d9628df17b | ||
|
|
f7abbd5f5a | ||
|
|
1544317fce | ||
|
|
172aa7461a | ||
|
|
76f5425452 | ||
|
|
87aa18d14c | ||
|
|
e10ab5ccb2 | ||
|
|
f774753213 | ||
|
|
a210c238f1 | ||
|
|
cdbbe0d49f | ||
|
|
c98e5a74de | ||
|
|
8b8d660b06 | ||
|
|
3c1a4afcb7 | ||
|
|
cabc9f5e2f | ||
|
|
28b8aff1ae | ||
|
|
e7487189cf | ||
|
|
92f53235f3 | ||
|
|
002050f35b | ||
|
|
394a3fbd8a | ||
|
|
af3a0332f9 | ||
|
|
0c80129466 | ||
|
|
ae35aaed5e | ||
|
|
9d251dbad5 | ||
|
|
a020138f6b | ||
|
|
701cdda38c | ||
|
|
919c4eee73 | ||
|
|
3e913495b1 | ||
|
|
bbb6f9e042 | ||
|
|
cdd6fb0864 | ||
|
|
780c065fa1 | ||
|
|
db7d9e1120 | ||
|
|
73566dff98 | ||
|
|
bd0a41107a | ||
|
|
62844ff637 | ||
|
|
5077b03a02 | ||
|
|
ffc2ff1d0e | ||
|
|
3aadab0925 | ||
|
|
85c00b8b43 | ||
|
|
e51792c65a | ||
|
|
1e74b2f53f | ||
|
|
886b5345d7 | ||
|
|
7d8b5374ad | ||
|
|
0c7730f4c5 | ||
|
|
a7aab714ba | ||
|
|
032b9a096e | ||
|
|
1f3d61dc7f | ||
|
|
32b173c653 | ||
|
|
8c4cd8c9aa | ||
|
|
73423ebd60 | ||
|
|
334e260b85 | ||
|
|
731957dab9 | ||
|
|
9c505fd75f | ||
|
|
25cec20f0d | ||
|
|
f05d501380 | ||
|
|
b02c34c6e5 | ||
|
|
801134b630 | ||
|
|
73d43007d4 | ||
|
|
1a9e05e6ab | ||
|
|
1006725b09 | ||
|
|
a4f9c49ab5 | ||
|
|
658622880f | ||
|
|
720fc491ce | ||
|
|
56feb77f2d | ||
|
|
233d263e13 | ||
|
|
dbd616fbe5 | ||
|
|
86c2e90e06 | ||
|
|
43610bbff4 | ||
|
|
29543bad4b | ||
|
|
2a047a469c | ||
|
|
83502f2d30 | ||
|
|
b60f679814 | ||
|
|
14e044093c | ||
|
|
3188038a7b | ||
|
|
ebf996acd8 | ||
|
|
0e0eccbd13 | ||
|
|
e834e93deb | ||
|
|
d37c164993 | ||
|
|
6daa7aa61d | ||
|
|
1110d3f8ef | ||
|
|
1d73d9d476 | ||
|
|
2da80954d6 | ||
|
|
e2fa66a38c | ||
|
|
94382ee418 | ||
|
|
94c92ef021 | ||
|
|
8c86849bb9 | ||
|
|
30909d57fb | ||
|
|
aaed8ab95e | ||
|
|
f24b20b133 | ||
|
|
2a53b8e59b | ||
|
|
ccaa1281a3 | ||
|
|
01843fe6c4 | ||
|
|
66fb672492 | ||
|
|
38ed953599 | ||
|
|
86ed71cb1f | ||
|
|
ccc31670ec | ||
|
|
0d8f5780dd | ||
|
|
a8a262640f | ||
|
|
72333f0888 | ||
|
|
a84083be40 | ||
|
|
9b8583e778 | ||
|
|
aeaa040920 | ||
|
|
222b67b1b0 | ||
|
|
1ee1710a96 | ||
|
|
a1e2ee7837 | ||
|
|
f04ade6d76 | ||
|
|
fee0811893 | ||
|
|
956003aa0a | ||
|
|
8fb28f9e2f | ||
|
|
a06a0ea059 | ||
|
|
4f71c1e8bf | ||
|
|
c56470c83f | ||
|
|
cc27032752 | ||
|
|
7c97902d4a | ||
|
|
23959c5b0e | ||
|
|
e726d5907e | ||
|
|
1337f54e4a | ||
|
|
471902e531 | ||
|
|
22d33360f6 | ||
|
|
db1e3b73d1 | ||
|
|
fe73db4ff6 | ||
|
|
9dd6eeee7b | ||
|
|
3c609658fd | ||
|
|
8b694432aa | ||
|
|
b32bd309ca | ||
|
|
78b5e6fd4b | ||
|
|
c5c0a8b193 | ||
|
|
b51eef2319 | ||
|
|
ebed9d31d7 | ||
|
|
16b251d3c9 | ||
|
|
5766bf479b | ||
|
|
8a9477f2bb | ||
|
|
2aaf878889 | ||
|
|
dfa499659e | ||
|
|
24b5f9b43a | ||
|
|
0ab7390a64 | ||
|
|
c7b820c376 | ||
|
|
ce41f2f8ba | ||
|
|
8bc3c46c34 | ||
|
|
b4473d6e24 | ||
|
|
910f3a6860 | ||
|
|
81335e5b4b | ||
|
|
55653c974e | ||
|
|
a5a87a79a8 | ||
|
|
a479bf9039 | ||
|
|
b7e19cc86a | ||
|
|
fe0742a628 | ||
|
|
29bae3a734 | ||
|
|
6aa6f04059 | ||
|
|
fc6d74e2b7 | ||
|
|
92e0d7895b | ||
|
|
250d5bfc24 | ||
|
|
dfda63138f | ||
|
|
8ab6c16f6e | ||
|
|
a6d51a60a8 | ||
|
|
3d37892296 | ||
|
|
1df54f071f | ||
|
|
b3253bacc4 | ||
|
|
4b1721721b | ||
|
|
2db07236db | ||
|
|
7295955397 | ||
|
|
f83709d96c | ||
|
|
a678a1c652 | ||
|
|
3d1336dc6c | ||
|
|
da1d0d81c2 | ||
|
|
335e18fe98 | ||
|
|
6e2539af09 | ||
|
|
fdc9f7d4d4 | ||
|
|
08c03e48cc | ||
|
|
6621f6fd91 |
@@ -2,7 +2,7 @@ blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: "IMPORTANT: Check development branch changelog first!!"
|
||||
url: https://gist.github.com/Koenkk/bfd4c3d1725a2cccacc11d6ba51008ba
|
||||
about: "Before submitting an issue, check if the issue hasn't already been solved in the development branch. Click 'Open' to see the release notes of the development branch. In case it is, you can read here how to switch to the development branch: https://www.zigbee2mqtt.io/advanced/more/switch-to-dev-branch.html"
|
||||
about: "Before submitting an issue, check that the issue hasn't already been solved in the development branch. Click 'Open' to see the release notes of the development branch. In case it is, you can read here how to switch to the development branch: https://www.zigbee2mqtt.io/advanced/more/switch-to-dev-branch.html"
|
||||
- name: Questions/discussion
|
||||
url: https://github.com/Koenkk/zigbee2mqtt/discussions/new
|
||||
about: Ask questions, discuss about devices or show things you made
|
||||
|
||||
@@ -7,12 +7,12 @@ body:
|
||||
attributes:
|
||||
value: |
|
||||
**IMPORTANT:** Before submitting:
|
||||
- Is your feature request related to the fronted? Then click [here](https://github.com/nurikk/zigbee2mqtt-frontend/issues/new?assignees=&labels=&template=feature_request.md&title=)
|
||||
- Is your feature request related to the frontend? Then click [here](https://github.com/nurikk/zigbee2mqtt-frontend/issues/new?assignees=&labels=&template=feature_request.md&title=)
|
||||
- type: textarea
|
||||
id: textarea1
|
||||
attributes:
|
||||
label: Is your feature request related to a problem? Please describe
|
||||
placeholder: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
placeholder: A clear and concise description of what the problem is. Eg. I'm always frustrated when [...]
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
||||
@@ -36,7 +36,7 @@ body:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: External defintion
|
||||
label: External definition
|
||||
description: See [Creating the external definition](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html#_2-creating-the-external-definition)
|
||||
render: shell
|
||||
validations:
|
||||
|
||||
@@ -7,11 +7,12 @@ body:
|
||||
value: |
|
||||
**IMPORTANT:** Before submitting:
|
||||
- You read the [FAQ](https://www.zigbee2mqtt.io/guide/faq/)
|
||||
- Are you using an EZSP adapter (e.g. Dongle-E/SkyConnect)? Try the [new driver](https://github.com/Koenkk/zigbee2mqtt/discussions/21462)
|
||||
- Zigbee2MQTT fails to start? Read [this](https://www.zigbee2mqtt.io/guide/installation/20_zigbee2mqtt-fails-to-start.html)
|
||||
- Make sure the bug also occurs in the [dev branch](https://www.zigbee2mqtt.io/advanced/more/switch-to-dev-branch.html)
|
||||
- Make sure you are using the [latest firmware](https://www.zigbee2mqtt.io/guide/adapters/#recommended) on your adapter
|
||||
- The issue has not been [reported already](https://github.com/Koenkk/zigbee2mqtt/issues)
|
||||
- Is your issue related to the fronted? Then click [here](https://github.com/nurikk/zigbee2mqtt-frontend/issues/new?assignees=&labels=bug%2Ctriage&template=bug_report.yaml&title=%5BBug%5D%3A+)
|
||||
- Is your issue related to the frontend? Then click [here](https://github.com/nurikk/zigbee2mqtt-frontend/issues/new?assignees=&labels=bug%2Ctriage&template=bug_report.yaml&title=%5BBug%5D%3A+)
|
||||
- If you are using the Home Assistant addon and are still on 1.18.1, check the first point of [this](https://github.com/Koenkk/zigbee2mqtt/releases/tag/1.19.0)
|
||||
- type: textarea
|
||||
id: what_happend
|
||||
@@ -30,7 +31,7 @@ body:
|
||||
id: reproduce
|
||||
attributes:
|
||||
label: How to reproduce it (minimal and precise)
|
||||
placeholder: First do this, than this..
|
||||
placeholder: First do this, then this..
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
|
||||
@@ -11,6 +11,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
if: (github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push'
|
||||
with:
|
||||
# Required for `release: merge dev -> master and promote dev`
|
||||
token: ${{secrets.GH_TOKEN}}
|
||||
- uses: actions/checkout@v4
|
||||
if: ((github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push') == false
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
@@ -96,9 +102,10 @@ jobs:
|
||||
TAG=${GITHUB_REF#refs/*/}
|
||||
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git fetch --unshallow
|
||||
git fetch origin
|
||||
git checkout master
|
||||
git merge -m 'chore: merge dev to master' origin/dev
|
||||
git merge --ff-only origin/dev
|
||||
git push origin master
|
||||
git checkout dev
|
||||
jq ".version = \"$TAG-dev\"" package.json > package.json.tmp
|
||||
|
||||
@@ -12,8 +12,8 @@ jobs:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days'
|
||||
stale-pr-message: 'This pull request is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days'
|
||||
stale-issue-message: 'This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 30 days'
|
||||
stale-pr-message: 'This pull request is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 30 days'
|
||||
days-before-stale: 180
|
||||
days-before-close: 30
|
||||
exempt-issue-labels: dont-stale
|
||||
|
||||
@@ -23,7 +23,8 @@ jobs:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
# connect-gzip-static@4.0.0 requires Node 20 >=
|
||||
- run: npx npm-check-updates -u -x connect-gzip-static
|
||||
# eslint: https://github.com/typescript-eslint/typescript-eslint/issues/8211
|
||||
- run: npx npm-check-updates -u -x connect-gzip-static -x eslint
|
||||
- run: rm -f package-lock.json
|
||||
- run: npm install
|
||||
- uses: peter-evans/create-pull-request@v6
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
".": "1.35.2"
|
||||
".": "1.37.0"
|
||||
}
|
||||
+185
@@ -1,5 +1,190 @@
|
||||
# Changelog
|
||||
|
||||
## [1.37.0](https://github.com/Koenkk/zigbee2mqtt/compare/1.36.1...1.37.0) (2024-05-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Unified logging across z2m/zh/zhc ([#21984](https://github.com/Koenkk/zigbee2mqtt/issues/21984)) ([73566df](https://github.com/Koenkk/zigbee2mqtt/commit/73566dff98e64038117ec71c7aaa897a4070474d))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Change `MQTT publish` log to `debug` ([#22063](https://github.com/Koenkk/zigbee2mqtt/issues/22063)) ([bbb6f9e](https://github.com/Koenkk/zigbee2mqtt/commit/bbb6f9e042a5c6cce78591e402d8ab0f7ab4195e))
|
||||
* Cleanup old logger leftovers ([#22069](https://github.com/Koenkk/zigbee2mqtt/issues/22069)) ([919c4ee](https://github.com/Koenkk/zigbee2mqtt/commit/919c4eee739d9c2dee855cf868e88f7848e6af36))
|
||||
* Convert logger into a class ([#22129](https://github.com/Koenkk/zigbee2mqtt/issues/22129)) ([3c1a4af](https://github.com/Koenkk/zigbee2mqtt/commit/3c1a4afcb723eecccf6ab9c3e04e1e0eca7b2cbb))
|
||||
* Disable automatic reconfigure when configureKey changes ([#22088](https://github.com/Koenkk/zigbee2mqtt/issues/22088)) ([9d251db](https://github.com/Koenkk/zigbee2mqtt/commit/9d251dbad5660f7663a79360a584df69d6f10875))
|
||||
* Fix crash when stopping in case MQTT client did not connect yet ([#22110](https://github.com/Koenkk/zigbee2mqtt/issues/22110)) ([92f5323](https://github.com/Koenkk/zigbee2mqtt/commit/92f53235f3e143ade8883dce01439f87913e2679))
|
||||
* **ignore:** fix 73566df ([cdd6fb0](https://github.com/Koenkk/zigbee2mqtt/commit/cdd6fb086419892e2725fc545b8015d83dea145d))
|
||||
* **ignore:** fix 73566dff98e64038117ec71c7aaa897a4070474d ([780c065](https://github.com/Koenkk/zigbee2mqtt/commit/780c065fa106358683bbef6fd72b0257adca5bae))
|
||||
* **ignore:** update dependencies ([#22007](https://github.com/Koenkk/zigbee2mqtt/issues/22007)) ([e748718](https://github.com/Koenkk/zigbee2mqtt/commit/e7487189cf6b2143a5606ce9bac7d4fa6e240587))
|
||||
* **ignore:** update dependencies ([#22331](https://github.com/Koenkk/zigbee2mqtt/issues/22331)) ([d7e2d98](https://github.com/Koenkk/zigbee2mqtt/commit/d7e2d98a5a79689cfdc06e1f9bd3b1ed10251132))
|
||||
* **ignore:** update zigbee-herdsman to 0.41.0 ([#22045](https://github.com/Koenkk/zigbee2mqtt/issues/22045)) ([bd0a411](https://github.com/Koenkk/zigbee2mqtt/commit/bd0a41107a18ab725d0d669af7993f2ad57cd9dd))
|
||||
* **ignore:** update zigbee-herdsman to 0.41.1 ([#22064](https://github.com/Koenkk/zigbee2mqtt/issues/22064)) ([3e91349](https://github.com/Koenkk/zigbee2mqtt/commit/3e913495b1a843cf0d67e4f2ffba975f56dd5f94))
|
||||
* **ignore:** update zigbee-herdsman to 0.41.2 ([#22087](https://github.com/Koenkk/zigbee2mqtt/issues/22087)) ([a020138](https://github.com/Koenkk/zigbee2mqtt/commit/a020138f6b4e90377d8bacf475fd33f62abb24e5))
|
||||
* **ignore:** update zigbee-herdsman to 0.42.0 ([#22121](https://github.com/Koenkk/zigbee2mqtt/issues/22121)) ([28b8aff](https://github.com/Koenkk/zigbee2mqtt/commit/28b8aff1ae8cabce8f61e34c85cd0b72b52a1e83))
|
||||
* **ignore:** update zigbee-herdsman to 0.42.1 ([#22143](https://github.com/Koenkk/zigbee2mqtt/issues/22143)) ([a210c23](https://github.com/Koenkk/zigbee2mqtt/commit/a210c238f1156bdbb56ba5fc223d0b2159c07273))
|
||||
* **ignore:** update zigbee-herdsman to 0.42.2 ([#22220](https://github.com/Koenkk/zigbee2mqtt/issues/22220)) ([76f5425](https://github.com/Koenkk/zigbee2mqtt/commit/76f54254529d13bfa0e9a40a0c7455b0c1e61bc8))
|
||||
* **ignore:** update zigbee-herdsman to 0.42.3 ([#22253](https://github.com/Koenkk/zigbee2mqtt/issues/22253)) ([f7abbd5](https://github.com/Koenkk/zigbee2mqtt/commit/f7abbd5f5ab878bea364cc0cda4222b8941fd487))
|
||||
* **ignore:** update zigbee-herdsman to 0.42.4 ([#22283](https://github.com/Koenkk/zigbee2mqtt/issues/22283)) ([a8f6bec](https://github.com/Koenkk/zigbee2mqtt/commit/a8f6bec673f43fd76c3495927c2c44e831ade6da))
|
||||
* **ignore:** update zigbee-herdsman to 0.42.5 ([#22285](https://github.com/Koenkk/zigbee2mqtt/issues/22285)) ([4f97a7a](https://github.com/Koenkk/zigbee2mqtt/commit/4f97a7ad2c12ec70ff459dc233d55bada734e7ab))
|
||||
* **ignore:** update zigbee-herdsman to 0.43.0 ([#22300](https://github.com/Koenkk/zigbee2mqtt/issues/22300)) ([75f9743](https://github.com/Koenkk/zigbee2mqtt/commit/75f9743528ac3f55b837548f234b29bdf8182a0d))
|
||||
* **ignore:** update zigbee-herdsman to 0.43.1 ([#22317](https://github.com/Koenkk/zigbee2mqtt/issues/22317)) ([389779d](https://github.com/Koenkk/zigbee2mqtt/commit/389779d4e332db1945ff7b490ec881eec71b4721))
|
||||
* **ignore:** update zigbee-herdsman to 0.44.0 ([#22339](https://github.com/Koenkk/zigbee2mqtt/issues/22339)) ([1b089ef](https://github.com/Koenkk/zigbee2mqtt/commit/1b089ef6ae364786602c485ea5deafad4e21f023))
|
||||
* **ignore:** update zigbee-herdsman to 0.45.0 ([#22365](https://github.com/Koenkk/zigbee2mqtt/issues/22365)) ([8e27ec2](https://github.com/Koenkk/zigbee2mqtt/commit/8e27ec24f664ca702eba93f90b42c9f4e74d16df))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.13.0 ([#22046](https://github.com/Koenkk/zigbee2mqtt/issues/22046)) ([db7d9e1](https://github.com/Koenkk/zigbee2mqtt/commit/db7d9e1120e01dd6b4e20bd352689eebb9a8912f))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.15.0 ([#22089](https://github.com/Koenkk/zigbee2mqtt/issues/22089)) ([ae35aae](https://github.com/Koenkk/zigbee2mqtt/commit/ae35aaed5ef3591f288019d9cb35d90a31aa4798))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.16.0 ([#22107](https://github.com/Koenkk/zigbee2mqtt/issues/22107)) ([394a3fb](https://github.com/Koenkk/zigbee2mqtt/commit/394a3fbd8aeb1dd4708326d8f854ac7db4b28dbf))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.17.0 ([#22122](https://github.com/Koenkk/zigbee2mqtt/issues/22122)) ([cabc9f5](https://github.com/Koenkk/zigbee2mqtt/commit/cabc9f5e2f6e22fb380e259093d29ec506a3b1f3))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.18.0 ([#22141](https://github.com/Koenkk/zigbee2mqtt/issues/22141)) ([8b8d660](https://github.com/Koenkk/zigbee2mqtt/commit/8b8d660b0622072696113c3a66458cbde1918838))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.19.0 ([#22153](https://github.com/Koenkk/zigbee2mqtt/issues/22153)) ([cdbbe0d](https://github.com/Koenkk/zigbee2mqtt/commit/cdbbe0d49fff21a3fb5000ecef25bb71b3d64360))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.19.1 ([#22171](https://github.com/Koenkk/zigbee2mqtt/issues/22171)) ([f774753](https://github.com/Koenkk/zigbee2mqtt/commit/f77475321300a11ca0d30525fe38afa7d079f42e))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.20.0 ([#22187](https://github.com/Koenkk/zigbee2mqtt/issues/22187)) ([e10ab5c](https://github.com/Koenkk/zigbee2mqtt/commit/e10ab5ccb272fdc3b2922fd28c043d84860137e1))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.21.0 ([#22199](https://github.com/Koenkk/zigbee2mqtt/issues/22199)) ([87aa18d](https://github.com/Koenkk/zigbee2mqtt/commit/87aa18d14cbf19a89d7d0dced6eb86393f0239af))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.22.0 ([#22221](https://github.com/Koenkk/zigbee2mqtt/issues/22221)) ([172aa74](https://github.com/Koenkk/zigbee2mqtt/commit/172aa7461ac507aa55dcd653d40873445c827f28))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.23.0 ([#22232](https://github.com/Koenkk/zigbee2mqtt/issues/22232)) ([1544317](https://github.com/Koenkk/zigbee2mqtt/commit/1544317fce5de3be71395af06450a5e997b39dab))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.24.0 ([#22254](https://github.com/Koenkk/zigbee2mqtt/issues/22254)) ([d9628df](https://github.com/Koenkk/zigbee2mqtt/commit/d9628df17b228a0908d7b95e35cbb97171c2485c))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.25.0 ([#22266](https://github.com/Koenkk/zigbee2mqtt/issues/22266)) ([80504b9](https://github.com/Koenkk/zigbee2mqtt/commit/80504b943d742a20481b458eb0fdc9cdf9452961))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.26.0 ([#22284](https://github.com/Koenkk/zigbee2mqtt/issues/22284)) ([4bf2ffa](https://github.com/Koenkk/zigbee2mqtt/commit/4bf2ffa7ddd0de3d70160ccefbaf483fc9a83a22))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.27.0 ([#22301](https://github.com/Koenkk/zigbee2mqtt/issues/22301)) ([df23240](https://github.com/Koenkk/zigbee2mqtt/commit/df232406838b991c87ddbaa6c28ebcedd6226fcf))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.28.0 ([#22318](https://github.com/Koenkk/zigbee2mqtt/issues/22318)) ([245afb1](https://github.com/Koenkk/zigbee2mqtt/commit/245afb112648c23c74c8e1e9d9b038c84d71ca40))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.29.0 ([#22325](https://github.com/Koenkk/zigbee2mqtt/issues/22325)) ([3755126](https://github.com/Koenkk/zigbee2mqtt/commit/37551265a204b6509b2534b5b0fcc9825b5188ac))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.32.0 ([#22377](https://github.com/Koenkk/zigbee2mqtt/issues/22377)) ([e03013c](https://github.com/Koenkk/zigbee2mqtt/commit/e03013c58180391d9f3fe6878712ab032896a94f))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.162 ([#22094](https://github.com/Koenkk/zigbee2mqtt/issues/22094)) ([0c80129](https://github.com/Koenkk/zigbee2mqtt/commit/0c801294661b0f6e592dd09f681c83bff376414d))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.163 ([#22288](https://github.com/Koenkk/zigbee2mqtt/issues/22288)) ([e5d632b](https://github.com/Koenkk/zigbee2mqtt/commit/e5d632bc9177c134d6856f9b5881f26be4ed4140))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.164 ([#22340](https://github.com/Koenkk/zigbee2mqtt/issues/22340)) ([10e6ad0](https://github.com/Koenkk/zigbee2mqtt/commit/10e6ad0e67a237693365c5bc8752ba3cbefe757f))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.165 ([#22366](https://github.com/Koenkk/zigbee2mqtt/issues/22366)) ([8a288a9](https://github.com/Koenkk/zigbee2mqtt/commit/8a288a91192bfa47aad80435a02cf3313da0b4bc))
|
||||
* Reduce MQTT publishing by excluding debug logging from `bridge/logging` ([#22066](https://github.com/Koenkk/zigbee2mqtt/issues/22066)) ([002050f](https://github.com/Koenkk/zigbee2mqtt/commit/002050f35bd6228a464482d17a7feee82f9f5d31))
|
||||
* Remove deprecated Home Assistant `color_mode` ([#22142](https://github.com/Koenkk/zigbee2mqtt/issues/22142)) ([c98e5a7](https://github.com/Koenkk/zigbee2mqtt/commit/c98e5a74de1c322533f9c0defa0bbf0b5627b775))
|
||||
|
||||
## [1.36.1](https://github.com/Koenkk/zigbee2mqtt/compare/1.36.0...1.36.1) (2024-04-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Add more Home Assistant entity icons ([#21652](https://github.com/Koenkk/zigbee2mqtt/issues/21652)) ([01843fe](https://github.com/Koenkk/zigbee2mqtt/commit/01843fe6c43210ade1d1dfcd134fdd979d47f194))
|
||||
* Fix `set` endpoint name conversion ([#21653](https://github.com/Koenkk/zigbee2mqtt/issues/21653)) ([66fb672](https://github.com/Koenkk/zigbee2mqtt/commit/66fb672492c8f049368d5dbda3925500941cd534))
|
||||
* Fix crash due to race condition during interview ([#21680](https://github.com/Koenkk/zigbee2mqtt/issues/21680)) ([2da8095](https://github.com/Koenkk/zigbee2mqtt/commit/2da80954d69c753bcfbef30c2579584d7a0f8779))
|
||||
* Fix incorrect update state when Z2M is restarted during OTA update https://github.com/Koenkk/zigbee2mqtt/issues/12794 ([1110d3f](https://github.com/Koenkk/zigbee2mqtt/commit/1110d3f8efff948811a980968f8dc84465500d4f))
|
||||
* Home Assistant: remove button name if `device_class` is present ([#21726](https://github.com/Koenkk/zigbee2mqtt/issues/21726)) ([14e0440](https://github.com/Koenkk/zigbee2mqtt/commit/14e044093c4f481fc7c4f58c35c68ebddddf621c))
|
||||
* **ignore:** fix tests ([29543ba](https://github.com/Koenkk/zigbee2mqtt/commit/29543bad4bb8768bb66c15f4f084b0d9ccc6c1bc))
|
||||
* **ignore:** update dependencies ([#21657](https://github.com/Koenkk/zigbee2mqtt/issues/21657)) ([8c86849](https://github.com/Koenkk/zigbee2mqtt/commit/8c86849bb992e1125e9fed301eef8655aa95466c))
|
||||
* **ignore:** Update dependencies ([#21740](https://github.com/Koenkk/zigbee2mqtt/issues/21740)) ([43610bb](https://github.com/Koenkk/zigbee2mqtt/commit/43610bbff44572937650ee03ccd239d084d9959f))
|
||||
* **ignore:** update dependencies ([#21832](https://github.com/Koenkk/zigbee2mqtt/issues/21832)) ([1006725](https://github.com/Koenkk/zigbee2mqtt/commit/1006725b09a6e07e60586409798de3cbc7f3c1f1))
|
||||
* **ignore:** update dependencies ([#21923](https://github.com/Koenkk/zigbee2mqtt/issues/21923)) ([8c4cd8c](https://github.com/Koenkk/zigbee2mqtt/commit/8c4cd8c9aa8777027d538a114fc9310b1f2fe7a2))
|
||||
* **ignore:** Update manufacturer codes ([#21729](https://github.com/Koenkk/zigbee2mqtt/issues/21729)) ([83502f2](https://github.com/Koenkk/zigbee2mqtt/commit/83502f2d30cf2d79f3ea8576ab065590da29abd8))
|
||||
* **ignore:** update zigbee-herdsman to 0.35.2 ([#21654](https://github.com/Koenkk/zigbee2mqtt/issues/21654)) ([ccaa128](https://github.com/Koenkk/zigbee2mqtt/commit/ccaa1281a35249874a065fd2d4975aeab93f05f4))
|
||||
* **ignore:** update zigbee-herdsman to 0.35.3 ([#21677](https://github.com/Koenkk/zigbee2mqtt/issues/21677)) ([30909d5](https://github.com/Koenkk/zigbee2mqtt/commit/30909d57fba7f7a45caeb14afcbf5f8ac724c876))
|
||||
* **ignore:** update zigbee-herdsman to 0.36.0 ([#21689](https://github.com/Koenkk/zigbee2mqtt/issues/21689)) ([1d73d9d](https://github.com/Koenkk/zigbee2mqtt/commit/1d73d9d4768d8511ca24066fe199d4096b0301ac))
|
||||
* **ignore:** update zigbee-herdsman to 0.36.1 ([#21713](https://github.com/Koenkk/zigbee2mqtt/issues/21713)) ([e834e93](https://github.com/Koenkk/zigbee2mqtt/commit/e834e93debbd343236e71e275bbd3726a3b4ae3c))
|
||||
* **ignore:** update zigbee-herdsman to 0.36.2 ([#21727](https://github.com/Koenkk/zigbee2mqtt/issues/21727)) ([ebf996a](https://github.com/Koenkk/zigbee2mqtt/commit/ebf996acd83f32668be7dfd3994c2976db52bc58))
|
||||
* **ignore:** update zigbee-herdsman to 0.37.0 ([#21743](https://github.com/Koenkk/zigbee2mqtt/issues/21743)) ([b60f679](https://github.com/Koenkk/zigbee2mqtt/commit/b60f679814294ce454e4e7c08fc3dd11b9a224ff))
|
||||
* **ignore:** update zigbee-herdsman to 0.38.0 ([#21777](https://github.com/Koenkk/zigbee2mqtt/issues/21777)) ([233d263](https://github.com/Koenkk/zigbee2mqtt/commit/233d263e132e7b046194dedd620ae687a815f1ec))
|
||||
* **ignore:** update zigbee-herdsman to 0.38.1 ([#21802](https://github.com/Koenkk/zigbee2mqtt/issues/21802)) ([6586228](https://github.com/Koenkk/zigbee2mqtt/commit/658622880ffc7eee1c27ba499ce75da03b25c216))
|
||||
* **ignore:** update zigbee-herdsman to 0.39.0 ([#21871](https://github.com/Koenkk/zigbee2mqtt/issues/21871)) ([801134b](https://github.com/Koenkk/zigbee2mqtt/commit/801134b6303ecea0d41254ac84a6f0f96393dc3f))
|
||||
* **ignore:** update zigbee-herdsman to 0.39.1 ([#21891](https://github.com/Koenkk/zigbee2mqtt/issues/21891)) ([f05d501](https://github.com/Koenkk/zigbee2mqtt/commit/f05d501380a5416fb610f6eeeea7c9b7a04394cb))
|
||||
* **ignore:** update zigbee-herdsman to 0.40.0 ([#21901](https://github.com/Koenkk/zigbee2mqtt/issues/21901)) ([334e260](https://github.com/Koenkk/zigbee2mqtt/commit/334e260b851535379548e52a61cbef5956411f46))
|
||||
* **ignore:** update zigbee-herdsman to 0.40.1 ([#21987](https://github.com/Koenkk/zigbee2mqtt/issues/21987)) ([0c7730f](https://github.com/Koenkk/zigbee2mqtt/commit/0c7730f4c50fb97313de35533140a8c99f6ad374))
|
||||
* **ignore:** update zigbee-herdsman to 0.40.2 ([#22000](https://github.com/Koenkk/zigbee2mqtt/issues/22000)) ([886b534](https://github.com/Koenkk/zigbee2mqtt/commit/886b5345d7c134f27aa14e259ce1cfd8423b8636))
|
||||
* **ignore:** update zigbee-herdsman to 0.40.3 ([#22010](https://github.com/Koenkk/zigbee2mqtt/issues/22010)) ([1e74b2f](https://github.com/Koenkk/zigbee2mqtt/commit/1e74b2f53ff7af8d1067265c966f75aef8a794f1))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.44.0 ([#21690](https://github.com/Koenkk/zigbee2mqtt/issues/21690)) ([6daa7aa](https://github.com/Koenkk/zigbee2mqtt/commit/6daa7aa61db7c6ceef707ab27afca14e31a2e79f))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.45.0 ([#21699](https://github.com/Koenkk/zigbee2mqtt/issues/21699)) ([d37c164](https://github.com/Koenkk/zigbee2mqtt/commit/d37c164993cd4b3bc2f027be3a3a2b9a5b30d200))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.46.0 ([#21714](https://github.com/Koenkk/zigbee2mqtt/issues/21714)) ([0e0eccb](https://github.com/Koenkk/zigbee2mqtt/commit/0e0eccbd139e38df31b0860af26c50032f629fae))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.47.0 ([#21728](https://github.com/Koenkk/zigbee2mqtt/issues/21728)) ([3188038](https://github.com/Koenkk/zigbee2mqtt/commit/3188038a7bf673b2053783910229e1e60b6f5921))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.48.0 ([#21753](https://github.com/Koenkk/zigbee2mqtt/issues/21753)) ([2a047a4](https://github.com/Koenkk/zigbee2mqtt/commit/2a047a469c70f17a3c12d0a6f6fc2de9744835e7))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.0.0 ([#21768](https://github.com/Koenkk/zigbee2mqtt/issues/21768)) ([dbd616f](https://github.com/Koenkk/zigbee2mqtt/commit/dbd616fbe5a62986d2c28bbcc9cea93d293da397))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.1.0 ([#21778](https://github.com/Koenkk/zigbee2mqtt/issues/21778)) ([56feb77](https://github.com/Koenkk/zigbee2mqtt/commit/56feb77f2d1c357d59245db3b7cc37520554ed41))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.1.1 ([#21789](https://github.com/Koenkk/zigbee2mqtt/issues/21789)) ([720fc49](https://github.com/Koenkk/zigbee2mqtt/commit/720fc491ce70f197778b5abe750ac2d779a9ad4c))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.10.0 ([#21996](https://github.com/Koenkk/zigbee2mqtt/issues/21996)) ([7d8b537](https://github.com/Koenkk/zigbee2mqtt/commit/7d8b5374adca3c97976e557f1d44e1b85728d575))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.11.0 ([#22013](https://github.com/Koenkk/zigbee2mqtt/issues/22013)) ([e51792c](https://github.com/Koenkk/zigbee2mqtt/commit/e51792c65afa5a2bc863bb619c0057fc7242b191))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.11.1 ([#22014](https://github.com/Koenkk/zigbee2mqtt/issues/22014)) ([85c00b8](https://github.com/Koenkk/zigbee2mqtt/commit/85c00b8b43e2716231203599fac8bf01662535d5))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.11.2 ([#22021](https://github.com/Koenkk/zigbee2mqtt/issues/22021)) ([3aadab0](https://github.com/Koenkk/zigbee2mqtt/commit/3aadab0925d52839baf6d884ea59d25ff083a015))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.2.1 ([#21801](https://github.com/Koenkk/zigbee2mqtt/issues/21801)) ([a4f9c49](https://github.com/Koenkk/zigbee2mqtt/commit/a4f9c49ab5ef647829b8f505df7f91df8fca5036))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.3.0 ([#21851](https://github.com/Koenkk/zigbee2mqtt/issues/21851)) ([1a9e05e](https://github.com/Koenkk/zigbee2mqtt/commit/1a9e05e6ab93a1e93024600a58c5b8e62544daf4))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.4.0 ([#21870](https://github.com/Koenkk/zigbee2mqtt/issues/21870)) ([73d4300](https://github.com/Koenkk/zigbee2mqtt/commit/73d43007d45ee3cfc2d49ff2f4475fef63560421))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.4.1 ([#21872](https://github.com/Koenkk/zigbee2mqtt/issues/21872)) ([b02c34c](https://github.com/Koenkk/zigbee2mqtt/commit/b02c34c6e52cf97364f6affce82b281cd40c1780))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.4.2 ([#21892](https://github.com/Koenkk/zigbee2mqtt/issues/21892)) ([25cec20](https://github.com/Koenkk/zigbee2mqtt/commit/25cec20f0d0b6201679b719bf8dec9e7ebb26b62))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.5.0 ([#21902](https://github.com/Koenkk/zigbee2mqtt/issues/21902)) ([73423eb](https://github.com/Koenkk/zigbee2mqtt/commit/73423ebd60a04f0caf7ac40743422cea3ef052e9))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.6.0 ([#21927](https://github.com/Koenkk/zigbee2mqtt/issues/21927)) ([32b173c](https://github.com/Koenkk/zigbee2mqtt/commit/32b173c6537a4cdc3c303e521c5fa159cfcfb58d))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.8.0 ([#21957](https://github.com/Koenkk/zigbee2mqtt/issues/21957)) ([032b9a0](https://github.com/Koenkk/zigbee2mqtt/commit/032b9a096eac7ba01a8eee188c042287db55bfed))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.9.0 ([#21970](https://github.com/Koenkk/zigbee2mqtt/issues/21970)) ([a7aab71](https://github.com/Koenkk/zigbee2mqtt/commit/a7aab714ba1861eab15d0df3228e222feb8fa996))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.161 ([#21660](https://github.com/Koenkk/zigbee2mqtt/issues/21660)) ([aaed8ab](https://github.com/Koenkk/zigbee2mqtt/commit/aaed8ab95ec031ad5ea1f00933cc5f79b58fce29))
|
||||
|
||||
## [1.36.0](https://github.com/Koenkk/zigbee2mqtt/compare/1.35.3...1.36.0) (2024-03-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add experimental Ember adapter @Nerivec https://github.com/Koenkk/zigbee-herdsman/pull/918 ([#21459](https://github.com/Koenkk/zigbee2mqtt/issues/21459)) ([c5c0a8b](https://github.com/Koenkk/zigbee2mqtt/commit/c5c0a8b1938ed51887b21ded06d584f200ba32d1))
|
||||
* Add support for button entity in Home Assistant ([#21372](https://github.com/Koenkk/zigbee2mqtt/issues/21372)) ([24b5f9b](https://github.com/Koenkk/zigbee2mqtt/commit/24b5f9b43af8303f87fd5f29e57cca4a850d7b1d))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* `trigger_count` & `power_outage_count` adjustments in HA ([#21492](https://github.com/Koenkk/zigbee2mqtt/issues/21492)) ([fe73db4](https://github.com/Koenkk/zigbee2mqtt/commit/fe73db4ff686915841fc045750c7c7c98457422d))
|
||||
* Add noise and noise_detected to HA lookup ([#21610](https://github.com/Koenkk/zigbee2mqtt/issues/21610)) ([9b8583e](https://github.com/Koenkk/zigbee2mqtt/commit/9b8583e778ffbd92ba82315d5a53ed68f51e4fc3))
|
||||
* Check if Zigbee2MQTT is running in `update.sh` ([#21494](https://github.com/Koenkk/zigbee2mqtt/issues/21494)) ([8b69443](https://github.com/Koenkk/zigbee2mqtt/commit/8b694432aaa229823674eb4933eded3f2ab1517a))
|
||||
* Fix endpoint matching for TuYa dummy endpoints ([#21576](https://github.com/Koenkk/zigbee2mqtt/issues/21576)) ([956003a](https://github.com/Koenkk/zigbee2mqtt/commit/956003aa0ab104f611cf3d0c61bec1c476ac17d0))
|
||||
* Fix parsing entity name that contains slash in the name and endpoint specification ([#21493](https://github.com/Koenkk/zigbee2mqtt/issues/21493)) ([3c60965](https://github.com/Koenkk/zigbee2mqtt/commit/3c609658fd788410270af3343f01cc47b6ef0ab1))
|
||||
* **ignore:** update dependencies ([#21347](https://github.com/Koenkk/zigbee2mqtt/issues/21347)) ([ce41f2f](https://github.com/Koenkk/zigbee2mqtt/commit/ce41f2f8ba89d31926cdce7d2d93ec19ffccf7fc))
|
||||
* **ignore:** update dependencies ([#21444](https://github.com/Koenkk/zigbee2mqtt/issues/21444)) ([16b251d](https://github.com/Koenkk/zigbee2mqtt/commit/16b251d3c9c0651436899de6b63033d98e7b1353))
|
||||
* **ignore:** update dependencies ([#21561](https://github.com/Koenkk/zigbee2mqtt/issues/21561)) ([c56470c](https://github.com/Koenkk/zigbee2mqtt/commit/c56470c83fd3b9ea366f67814e09146bcac6d188))
|
||||
* **ignore:** update zigbee-herdsman to 0.33.9 ([#21386](https://github.com/Koenkk/zigbee2mqtt/issues/21386)) ([dfa4996](https://github.com/Koenkk/zigbee2mqtt/commit/dfa499659ec69db40ccc3d5c1dff874f446587d0))
|
||||
* **ignore:** update zigbee-herdsman to 0.34.1 ([#21461](https://github.com/Koenkk/zigbee2mqtt/issues/21461)) ([b51eef2](https://github.com/Koenkk/zigbee2mqtt/commit/b51eef2319adf2b26b7b9a4a279adf2b671dd380))
|
||||
* **ignore:** update zigbee-herdsman to 0.34.2 ([#21478](https://github.com/Koenkk/zigbee2mqtt/issues/21478)) ([78b5e6f](https://github.com/Koenkk/zigbee2mqtt/commit/78b5e6fd4b11ba138f83e848c60ac017fe5b10f2))
|
||||
* **ignore:** update zigbee-herdsman to 0.34.3 ([#21530](https://github.com/Koenkk/zigbee2mqtt/issues/21530)) ([e726d59](https://github.com/Koenkk/zigbee2mqtt/commit/e726d5907e86ca3fabef4491579994d9e89f0fe0))
|
||||
* **ignore:** update zigbee-herdsman to 0.34.4 ([#21545](https://github.com/Koenkk/zigbee2mqtt/issues/21545)) ([23959c5](https://github.com/Koenkk/zigbee2mqtt/commit/23959c5b0ec7e36183400920617e3052cd9bf17a))
|
||||
* **ignore:** update zigbee-herdsman to 0.34.5 ([#21563](https://github.com/Koenkk/zigbee2mqtt/issues/21563)) ([cc27032](https://github.com/Koenkk/zigbee2mqtt/commit/cc2703275272c268b1f04ce75488027d6c70b870))
|
||||
* **ignore:** update zigbee-herdsman to 0.34.6 ([#21567](https://github.com/Koenkk/zigbee2mqtt/issues/21567)) ([4f71c1e](https://github.com/Koenkk/zigbee2mqtt/commit/4f71c1e8bf3fe430cc03d3054a6e2a75ba6ec7de))
|
||||
* **ignore:** update zigbee-herdsman to 0.34.7 ([#21575](https://github.com/Koenkk/zigbee2mqtt/issues/21575)) ([a06a0ea](https://github.com/Koenkk/zigbee2mqtt/commit/a06a0ea059354ac43de47c9d4e6d573bd6186288))
|
||||
* **ignore:** update zigbee-herdsman to 0.34.8 ([#21588](https://github.com/Koenkk/zigbee2mqtt/issues/21588)) ([fee0811](https://github.com/Koenkk/zigbee2mqtt/commit/fee0811893a6bc90b86978e5c80cdb4e1a23ef70))
|
||||
* **ignore:** update zigbee-herdsman to 0.34.9 ([#21602](https://github.com/Koenkk/zigbee2mqtt/issues/21602)) ([222b67b](https://github.com/Koenkk/zigbee2mqtt/commit/222b67b1b0c9151222c4d9cc2e531bf74a080ba6))
|
||||
* **ignore:** update zigbee-herdsman to 0.35.0 ([#21621](https://github.com/Koenkk/zigbee2mqtt/issues/21621)) ([a84083b](https://github.com/Koenkk/zigbee2mqtt/commit/a84083be404a54303eb9337cc86c3938a1d5d41e))
|
||||
* **ignore:** update zigbee-herdsman to 0.35.1 ([#21636](https://github.com/Koenkk/zigbee2mqtt/issues/21636)) ([0d8f578](https://github.com/Koenkk/zigbee2mqtt/commit/0d8f5780ddf05d176b199a152b3a1a212bca6810))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.29.1 ([#21290](https://github.com/Koenkk/zigbee2mqtt/issues/21290)) ([55653c9](https://github.com/Koenkk/zigbee2mqtt/commit/55653c974ef72e8b5d8107d1b2e1b67efb70e4e1))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.30.0 ([#21315](https://github.com/Koenkk/zigbee2mqtt/issues/21315)) ([910f3a6](https://github.com/Koenkk/zigbee2mqtt/commit/910f3a68603bf4b7f31d7a80370816d02f7126b8))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.31.0 ([#21359](https://github.com/Koenkk/zigbee2mqtt/issues/21359)) ([c7b820c](https://github.com/Koenkk/zigbee2mqtt/commit/c7b820c3763e6052cc6755e0e12926c316de7f42))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.32.0 ([#21373](https://github.com/Koenkk/zigbee2mqtt/issues/21373)) ([0ab7390](https://github.com/Koenkk/zigbee2mqtt/commit/0ab7390a642a723fc4e1847bbd95ea12487388ab))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.33.0 ([#21387](https://github.com/Koenkk/zigbee2mqtt/issues/21387)) ([2aaf878](https://github.com/Koenkk/zigbee2mqtt/commit/2aaf878889b4211b8a470c847721c75b5a91e39e))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.33.1 ([#21400](https://github.com/Koenkk/zigbee2mqtt/issues/21400)) ([8a9477f](https://github.com/Koenkk/zigbee2mqtt/commit/8a9477f2bb6afbfeacf64547c8bfe4ae4f18ca7f))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.33.2 ([#21416](https://github.com/Koenkk/zigbee2mqtt/issues/21416)) ([5766bf4](https://github.com/Koenkk/zigbee2mqtt/commit/5766bf479ba548d819f87de2046f554b6de15945))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.34.0 ([#21456](https://github.com/Koenkk/zigbee2mqtt/issues/21456)) ([ebed9d3](https://github.com/Koenkk/zigbee2mqtt/commit/ebed9d31d710ac6853e3eee6b677cfe6e84fd556))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.35.0 ([#21487](https://github.com/Koenkk/zigbee2mqtt/issues/21487)) ([b32bd30](https://github.com/Koenkk/zigbee2mqtt/commit/b32bd309ca7748d4dcb0b3ca5b3428578a86487c))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.36.0 ([#21504](https://github.com/Koenkk/zigbee2mqtt/issues/21504)) ([22d3336](https://github.com/Koenkk/zigbee2mqtt/commit/22d33360f6966625afb9f456d5ffe96c42baaa3f))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.37.0 ([#21533](https://github.com/Koenkk/zigbee2mqtt/issues/21533)) ([1337f54](https://github.com/Koenkk/zigbee2mqtt/commit/1337f54e4a49ac9e28b9c3eb780829915b59cb57))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.37.1 ([#21549](https://github.com/Koenkk/zigbee2mqtt/issues/21549)) ([7c97902](https://github.com/Koenkk/zigbee2mqtt/commit/7c97902d4a088d680010511148b67ac19d05b91a))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.40.0 ([#21603](https://github.com/Koenkk/zigbee2mqtt/issues/21603)) ([aeaa040](https://github.com/Koenkk/zigbee2mqtt/commit/aeaa0409203e19d54511601cf7e0124db6a09778))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.41.0 ([#21622](https://github.com/Koenkk/zigbee2mqtt/issues/21622)) ([72333f0](https://github.com/Koenkk/zigbee2mqtt/commit/72333f0888b5be3abacb286ed0a227438d593563))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.42.0 ([#21637](https://github.com/Koenkk/zigbee2mqtt/issues/21637)) ([ccc3167](https://github.com/Koenkk/zigbee2mqtt/commit/ccc31670ec5b93bddaa8cef3f965fd8cc77e51a4))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.157 ([#21269](https://github.com/Koenkk/zigbee2mqtt/issues/21269)) ([a479bf9](https://github.com/Koenkk/zigbee2mqtt/commit/a479bf9039248d0b0ebc7e0f65dfc8ff342090d4))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.158 ([#21356](https://github.com/Koenkk/zigbee2mqtt/issues/21356)) ([8bc3c46](https://github.com/Koenkk/zigbee2mqtt/commit/8bc3c46c34e1c13c893b5d52143f6e357ff58546))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.159 ([#21623](https://github.com/Koenkk/zigbee2mqtt/issues/21623)) ([a8a2626](https://github.com/Koenkk/zigbee2mqtt/commit/a8a262640ff075e92bfb8d0491f4f9efe8f33ce2))
|
||||
* Improve nested group and device name match ([#21521](https://github.com/Koenkk/zigbee2mqtt/issues/21521)) ([471902e](https://github.com/Koenkk/zigbee2mqtt/commit/471902e53134dbf2baddb9c37e2c20105b8f0043))
|
||||
* Match endpoint name in the MQTT topic based on endpoints listed in the definition ([#21214](https://github.com/Koenkk/zigbee2mqtt/issues/21214)) ([a5a87a7](https://github.com/Koenkk/zigbee2mqtt/commit/a5a87a79a80aaa261a34a8fb541f47347c4ca729))
|
||||
* Only configure routers on startup https://github.com/Koenkk/zigbee2mqtt/issues/21446 ([db1e3b7](https://github.com/Koenkk/zigbee2mqtt/commit/db1e3b73d1d4a2a98879ab10e5b18f84b30bd3bc))
|
||||
* Remove dependency on predefined list of endpoints (`parseEntityID()` function) ([#21264](https://github.com/Koenkk/zigbee2mqtt/issues/21264)) ([81335e5](https://github.com/Koenkk/zigbee2mqtt/commit/81335e5b4b0ed0e1dacdd764de1711f5a1481f7f))
|
||||
* Remove predefined endpoint list, remove limits on device friendly name ([#21325](https://github.com/Koenkk/zigbee2mqtt/issues/21325)) ([b4473d6](https://github.com/Koenkk/zigbee2mqtt/commit/b4473d6e24de8dc2db0ae7cb7bbb014788854ae4))
|
||||
|
||||
## [1.35.3](https://github.com/Koenkk/zigbee2mqtt/compare/1.35.2...1.35.3) (2024-02-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ignore:** set zhc logger ([f83709d](https://github.com/Koenkk/zigbee2mqtt/commit/f83709d96c4a98b2c5f34a2e06fe2ea26f9cb9d5))
|
||||
* **ignore:** update dependencies ([#21204](https://github.com/Koenkk/zigbee2mqtt/issues/21204)) ([1df54f0](https://github.com/Koenkk/zigbee2mqtt/commit/1df54f071f301240f4a7efeb8db87a9a66babf44))
|
||||
* **ignore:** update zigbee-herdsman to 0.33.7 ([#21178](https://github.com/Koenkk/zigbee2mqtt/issues/21178)) ([3d1336d](https://github.com/Koenkk/zigbee2mqtt/commit/3d1336dc6cb3f73810f4ed467df443e46cca23e0))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.26.0 ([#21145](https://github.com/Koenkk/zigbee2mqtt/issues/21145)) ([08c03e4](https://github.com/Koenkk/zigbee2mqtt/commit/08c03e48cc47b344c3f5664147afd3c4fe8c1802))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.27.0 ([#21184](https://github.com/Koenkk/zigbee2mqtt/issues/21184)) ([a678a1c](https://github.com/Koenkk/zigbee2mqtt/commit/a678a1c652a1a77fe80245d5944945ab7c75352b))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.27.1 ([#21189](https://github.com/Koenkk/zigbee2mqtt/issues/21189)) ([4b17217](https://github.com/Koenkk/zigbee2mqtt/commit/4b1721721b1580115ff952a2f49cd0c4e0dabafb))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.28.0 ([#21207](https://github.com/Koenkk/zigbee2mqtt/issues/21207)) ([3d37892](https://github.com/Koenkk/zigbee2mqtt/commit/3d3789229692eb522ef3f4b5d920f90336cf2be9))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.28.1 ([#21222](https://github.com/Koenkk/zigbee2mqtt/issues/21222)) ([a6d51a6](https://github.com/Koenkk/zigbee2mqtt/commit/a6d51a60a8b338f1245a17d228212364a6bf235c))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.28.2 ([#21245](https://github.com/Koenkk/zigbee2mqtt/issues/21245)) ([250d5bf](https://github.com/Koenkk/zigbee2mqtt/commit/250d5bfc24c146e6055db7f069770af02fccca7c))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.28.3 ([#21265](https://github.com/Koenkk/zigbee2mqtt/issues/21265)) ([29bae3a](https://github.com/Koenkk/zigbee2mqtt/commit/29bae3a73421c18c8437d21d727d70f10e08a1e9))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.153 ([#21187](https://github.com/Koenkk/zigbee2mqtt/issues/21187)) ([7295955](https://github.com/Koenkk/zigbee2mqtt/commit/7295955397dd829a15de08081c08ed236dee2c21))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.154 ([#21188](https://github.com/Koenkk/zigbee2mqtt/issues/21188)) ([2db0723](https://github.com/Koenkk/zigbee2mqtt/commit/2db07236dbc3b8ed2817209c72fd7103efb5fba7))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.155 ([#21228](https://github.com/Koenkk/zigbee2mqtt/issues/21228)) ([8ab6c16](https://github.com/Koenkk/zigbee2mqtt/commit/8ab6c16f6ecbf8dde9a208ea51f48603f5e21b68))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.156 ([#21229](https://github.com/Koenkk/zigbee2mqtt/issues/21229)) ([dfda631](https://github.com/Koenkk/zigbee2mqtt/commit/dfda63138ff4e0c6a0b28503d3e9c9482991b8b8))
|
||||
|
||||
## [1.35.2](https://github.com/Koenkk/zigbee2mqtt/compare/1.35.1...1.35.2) (2024-01-31)
|
||||
|
||||
|
||||
|
||||
Executable → Regular
+120
-1
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 25 KiB |
+7
-5
@@ -8,6 +8,8 @@ import utils from './util/utils';
|
||||
import stringify from 'json-stable-stringify-without-jsonify';
|
||||
import assert from 'assert';
|
||||
import bind from 'bind-decorator';
|
||||
import {setLogger as zhcSetLogger} from 'zigbee-herdsman-converters';
|
||||
import {setLogger as zhSetLogger} from 'zigbee-herdsman';
|
||||
|
||||
// Extensions
|
||||
import ExtensionFrontend from './extension/frontend';
|
||||
@@ -58,7 +60,8 @@ export class Controller {
|
||||
private extensionArgs: ExtensionArgs;
|
||||
|
||||
constructor(restartCallback: () => void, exitCallback: (code: number, restart: boolean) => void) {
|
||||
logger.init();
|
||||
zhSetLogger(logger);
|
||||
zhcSetLogger(logger);
|
||||
this.eventBus = new EventBus();
|
||||
this.zigbee = new Zigbee(this.eventBus);
|
||||
this.mqtt = new MQTT(this.eventBus);
|
||||
@@ -95,7 +98,6 @@ export class Controller {
|
||||
|
||||
async start(): Promise<void> {
|
||||
this.state.start();
|
||||
logger.logOutput();
|
||||
|
||||
const info = await utils.getZigbee2MQTTVersion();
|
||||
logger.info(`Starting Zigbee2MQTT version ${info.version} (commit #${info.commitHash})`);
|
||||
@@ -135,9 +137,9 @@ export class Controller {
|
||||
// Enable zigbee join
|
||||
try {
|
||||
if (settings.get().permit_join) {
|
||||
logger.warn('`permit_join` set to `true` in configuration.yaml.');
|
||||
logger.warn('Allowing new devices to join.');
|
||||
logger.warn('Set `permit_join` to `false` once you joined all devices.');
|
||||
logger.warning('`permit_join` set to `true` in configuration.yaml.');
|
||||
logger.warning('Allowing new devices to join.');
|
||||
logger.warning('Set `permit_join` to `false` once you joined all devices.');
|
||||
}
|
||||
|
||||
await this.zigbee.permitJoin(settings.get().permit_join);
|
||||
|
||||
@@ -87,7 +87,7 @@ export default class Availability extends Extension {
|
||||
logger.debug(`Successfully pinged '${device.name}' (attempt ${i + 1}/${attempts})`);
|
||||
break;
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to ping '${device.name}' (attempt ${i + 1}/${attempts}, ${error.message})`);
|
||||
logger.warning(`Failed to ping '${device.name}' (attempt ${i + 1}/${attempts}, ${error.message})`);
|
||||
// Try again in 3 seconds.
|
||||
const lastAttempt = i - 1 === attempts;
|
||||
!lastAttempt && await utils.sleep(3);
|
||||
@@ -215,7 +215,7 @@ export default class Availability extends Extension {
|
||||
const options: KeyValue = device.options;
|
||||
const state = this.state.get(device);
|
||||
const meta: zhc.Tz.Meta = {
|
||||
message: this.state.get(device), mapped: device.definition, logger, endpoint_name: null,
|
||||
message: this.state.get(device), mapped: device.definition, endpoint_name: null,
|
||||
options, state, device: device.zh,
|
||||
};
|
||||
await converter?.convertGet?.(device.endpoint(), item.keys[0], meta)
|
||||
|
||||
+23
-20
@@ -97,12 +97,12 @@ const pollOnMessage: PollOnMessage = [
|
||||
read: {cluster: 'genLevelCtrl', attributes: ['currentLevel']},
|
||||
// When the bound devices/members of group have the following manufacturerIDs
|
||||
manufacturerIDs: [
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.Philips,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.ATMEL,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.MUELLER_LICHT_INT,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.TELINK,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.BUSCH_JAEGER,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.MUELLER_LICHT_INTERNATIONAL_INC,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.TELINK_MICRO,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.BUSCH_JAEGER_ELEKTRO,
|
||||
],
|
||||
manufacturerNames: [
|
||||
'GLEDOPTO',
|
||||
@@ -133,12 +133,12 @@ const pollOnMessage: PollOnMessage = [
|
||||
},
|
||||
read: {cluster: 'genOnOff', attributes: ['onOff']},
|
||||
manufacturerIDs: [
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.Philips,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.ATMEL,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.MUELLER_LICHT_INT,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.TELINK,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.BUSCH_JAEGER,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.MUELLER_LICHT_INTERNATIONAL_INC,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.TELINK_MICRO,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.BUSCH_JAEGER_ELEKTRO,
|
||||
],
|
||||
manufacturerNames: [
|
||||
'GLEDOPTO',
|
||||
@@ -165,11 +165,11 @@ const pollOnMessage: PollOnMessage = [
|
||||
},
|
||||
},
|
||||
manufacturerIDs: [
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.Philips,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.ATMEL,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.MUELLER_LICHT_INT,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.TELINK,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.MUELLER_LICHT_INTERNATIONAL_INC,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.TELINK_MICRO,
|
||||
// Note: ManufacturerCode.BUSCH_JAEGER is left out intentionally here as their devices don't support colors
|
||||
],
|
||||
manufacturerNames: [
|
||||
@@ -222,25 +222,28 @@ export default class Bind extends Extension {
|
||||
const message = utils.parseJSON(data.message, data.message);
|
||||
|
||||
let error = null;
|
||||
const parsedSource = utils.parseEntityID(sourceKey);
|
||||
const parsedTarget = utils.parseEntityID(targetKey);
|
||||
const source = this.zigbee.resolveEntity(parsedSource.ID);
|
||||
const target = targetKey === 'default_bind_group' ?
|
||||
defaultBindGroup : this.zigbee.resolveEntity(parsedTarget.ID);
|
||||
const parsedSource = this.zigbee.resolveEntityAndEndpoint(sourceKey);
|
||||
const parsedTarget = this.zigbee.resolveEntityAndEndpoint(targetKey);
|
||||
const source = parsedSource.entity;
|
||||
const target = targetKey === 'default_bind_group' ? defaultBindGroup : parsedTarget.entity;
|
||||
const responseData: KeyValue = {from: sourceKey, to: targetKey};
|
||||
|
||||
if (!source || !(source instanceof Device)) {
|
||||
error = `Source device '${sourceKey}' does not exist`;
|
||||
} else if (parsedSource.endpointID && !parsedSource.endpoint) {
|
||||
error = `Source device '${parsedSource.ID}' does not have endpoint '${parsedSource.endpointID}'`;
|
||||
} else if (!target) {
|
||||
error = `Target device or group '${targetKey}' does not exist`;
|
||||
} else if (target instanceof Device && parsedTarget.endpointID && !parsedTarget.endpoint) {
|
||||
error = `Target device '${parsedTarget.ID}' does not have endpoint '${parsedTarget.endpointID}'`;
|
||||
} else {
|
||||
const successfulClusters: string[] = [];
|
||||
const failedClusters = [];
|
||||
const attemptedClusters = [];
|
||||
|
||||
const bindSource: zh.Endpoint = source.endpoint(parsedSource.endpoint);
|
||||
const bindSource: zh.Endpoint = parsedSource.endpoint;
|
||||
let bindTarget: number | zh.Group | zh.Endpoint = null;
|
||||
if (target instanceof Device) bindTarget = target.endpoint(parsedTarget.endpoint);
|
||||
if (target instanceof Device) bindTarget = parsedTarget.endpoint;
|
||||
else if (target instanceof Group) bindTarget = target.zh;
|
||||
else bindTarget = Number(target.ID);
|
||||
|
||||
@@ -391,7 +394,7 @@ export default class Bind extends Extension {
|
||||
await endpoint.configureReporting(bind.cluster.name, items);
|
||||
logger.info(`Successfully setup reporting for '${entity}' cluster '${bind.cluster.name}'`);
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to setup reporting for '${entity}' cluster '${bind.cluster.name}'`);
|
||||
logger.warning(`Failed to setup reporting for '${entity}' cluster '${bind.cluster.name}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -433,7 +436,7 @@ export default class Bind extends Extension {
|
||||
await endpoint.configureReporting(cluster, items);
|
||||
logger.info(`Successfully disabled reporting for '${entity}' cluster '${cluster}'`);
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to disable reporting for '${entity}' cluster '${cluster}'`);
|
||||
logger.warning(`Failed to disable reporting for '${entity}' cluster '${cluster}'`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+32
-10
@@ -13,6 +13,7 @@ import data from '../util/data';
|
||||
import JSZip from 'jszip';
|
||||
import fs from 'fs';
|
||||
import * as zhc from 'zigbee-herdsman-converters';
|
||||
import winston from 'winston';
|
||||
|
||||
const requestRegex = new RegExp(`${settings.get().mqtt.base_topic}/bridge/request/(.*)`);
|
||||
|
||||
@@ -28,6 +29,8 @@ export default class Bridge extends Extension {
|
||||
private coordinatorVersion: zh.CoordinatorVersion;
|
||||
private restartRequired = false;
|
||||
private lastJoinedDeviceIeeeAddr: string;
|
||||
private lastBridgeLoggingPayload: string;
|
||||
private logTransport: winston.transport;
|
||||
private requestLookup: {[key: string]: (message: KeyValue | string) => Promise<MQTTResponse>};
|
||||
|
||||
override async start(): Promise<void> {
|
||||
@@ -59,15 +62,23 @@ export default class Bridge extends Extension {
|
||||
};
|
||||
|
||||
const mqtt = this.mqtt;
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const self = this;
|
||||
class EventTransport extends Transport {
|
||||
log(info: {message: string, level: string}, callback: () => void): void {
|
||||
const payload = stringify({message: info.message, level: info.level});
|
||||
mqtt.publish(`bridge/logging`, payload, {}, settings.get().mqtt.base_topic, true);
|
||||
callback();
|
||||
log(info: {message: string, level: string}, next: () => void): void {
|
||||
if (info.level !== 'debug') {
|
||||
const payload = stringify({message: info.message, level: info.level});
|
||||
if (payload !== self.lastBridgeLoggingPayload) {
|
||||
self.lastBridgeLoggingPayload = payload;
|
||||
mqtt.publish(`bridge/logging`, payload, {}, settings.get().mqtt.base_topic, true);
|
||||
}
|
||||
}
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
logger.addTransport(new EventTransport());
|
||||
this.logTransport = new EventTransport();
|
||||
logger.addTransport(this.logTransport);
|
||||
|
||||
this.zigbee2mqttVersion = await utils.getZigbee2MQTTVersion();
|
||||
this.zigbeeHerdsmanVersion = await utils.getDependencyVersion('zigbee-herdsman');
|
||||
@@ -118,6 +129,11 @@ export default class Bridge extends Extension {
|
||||
this.eventBus.onMQTTMessage(this, this.onMQTTMessage);
|
||||
}
|
||||
|
||||
override async stop(): Promise<void> {
|
||||
super.stop();
|
||||
logger.removeTransport(this.logTransport);
|
||||
}
|
||||
|
||||
@bind async onMQTTMessage(data: eventdata.MQTTMessage): Promise<void> {
|
||||
const match = data.topic.match(requestRegex);
|
||||
const key = match?.[1]?.toLowerCase();
|
||||
@@ -430,8 +446,13 @@ export default class Bridge extends Extension {
|
||||
throw new Error(`Invalid payload`);
|
||||
}
|
||||
|
||||
const parsedID = utils.parseEntityID(message.id);
|
||||
const endpoint = (this.getEntity('device', parsedID.ID) as Device).endpoint(parsedID.endpoint);
|
||||
const device = this.zigbee.resolveEntityAndEndpoint(message.id);
|
||||
if (!device.entity) throw new Error(`Device '${message.id}' does not exist`);
|
||||
|
||||
const endpoint = device.endpoint;
|
||||
if (device.endpointID && !endpoint) {
|
||||
throw new Error(`Device '${device.ID}' does not have endpoint '${device.endpointID}'`);
|
||||
}
|
||||
|
||||
const coordinatorEndpoint = this.zigbee.firstCoordinatorEndpoint();
|
||||
await endpoint.bind(message.cluster, coordinatorEndpoint);
|
||||
@@ -457,8 +478,9 @@ export default class Bridge extends Extension {
|
||||
throw new Error(`Invalid payload`);
|
||||
}
|
||||
|
||||
const parsedID = utils.parseEntityID(message.id);
|
||||
const device = this.getEntity('device', parsedID.ID) as Device;
|
||||
const device = this.zigbee.resolveEntityAndEndpoint(message.id).entity as Device;
|
||||
if (!device) throw new Error(`Device '${message.id}' does not exist`);
|
||||
|
||||
const source = await zhc.generateExternalDefinitionSource(device.zh);
|
||||
|
||||
return utils.getResponse(message, {id: message.id, source}, null);
|
||||
@@ -536,7 +558,7 @@ export default class Bridge extends Extension {
|
||||
}
|
||||
|
||||
if (force) {
|
||||
await entity.zh.removeFromDatabase();
|
||||
entity.zh.removeFromDatabase();
|
||||
} else {
|
||||
await entity.zh.removeFromNetwork();
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ export default class Configure extends Extension {
|
||||
}
|
||||
|
||||
if (!device.definition || !device.definition.configure) {
|
||||
logger.warn(`Skipping configure of '${device.name}', device does not require this.`);
|
||||
logger.warning(`Skipping configure of '${device.name}', device does not require this.`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,9 @@ export default class Configure extends Extension {
|
||||
|
||||
override async start(): Promise<void> {
|
||||
setImmediate(async () => {
|
||||
for (const device of this.zigbee.devices(false)) {
|
||||
// Only configure routers on startup, end devices are likely sleeping and
|
||||
// will reconfigure once they send a message
|
||||
for (const device of this.zigbee.devices(false).filter((d) => d.zh.type === 'Router')) {
|
||||
// Sleep 10 seconds between configuring on startup to not DDoS the coordinator
|
||||
// when many devices have to be configured.
|
||||
await utils.sleep(10);
|
||||
@@ -94,8 +96,7 @@ export default class Configure extends Extension {
|
||||
return;
|
||||
}
|
||||
|
||||
if (device.zh.meta?.hasOwnProperty('configured') &&
|
||||
device.zh.meta.configured === zhc.getConfigureKey(device.definition)) {
|
||||
if (device.zh.meta?.hasOwnProperty('configured')) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -117,7 +118,7 @@ export default class Configure extends Extension {
|
||||
|
||||
logger.info(`Configuring '${device.name}'`);
|
||||
try {
|
||||
await device.definition.configure(device.zh, this.zigbee.firstCoordinatorEndpoint(), logger);
|
||||
await device.definition.configure(device.zh, this.zigbee.firstCoordinatorEndpoint(), device.definition);
|
||||
logger.info(`Successfully configured '${device.name}'`);
|
||||
device.zh.meta.configured = zhc.getConfigureKey(device.definition);
|
||||
device.zh.save();
|
||||
|
||||
+22
-10
@@ -68,10 +68,13 @@ export default class Groups extends Extension {
|
||||
const groupID = settingGroup.ID;
|
||||
const zigbeeGroup = zigbeeGroups.find((g) => g.ID === groupID) || this.zigbee.createGroup(groupID);
|
||||
const settingsEndpoint = settingGroup.devices.map((d) => {
|
||||
const parsed = utils.parseEntityID(d);
|
||||
const entity = this.zigbee.resolveEntity(parsed.ID) as Device;
|
||||
const parsed = this.zigbee.resolveEntityAndEndpoint(d);
|
||||
const entity = parsed.entity as Device;
|
||||
if (!entity) logger.error(`Cannot find '${d}' of group '${settingGroup.friendly_name}'`);
|
||||
return {'endpoint': entity?.endpoint(parsed.endpoint), 'name': entity?.name};
|
||||
if (parsed.endpointID && !parsed.endpoint) {
|
||||
logger.error(`Cannot find endpoint '${parsed.endpointID}' of device '${parsed.ID}'`);
|
||||
}
|
||||
return {'endpoint': parsed.endpoint, 'name': entity?.name};
|
||||
}).filter((e) => e.endpoint != null);
|
||||
|
||||
// In settings but not in zigbee
|
||||
@@ -109,8 +112,9 @@ export default class Groups extends Extension {
|
||||
const payload: KeyValue = {};
|
||||
|
||||
let endpointName: string = null;
|
||||
const endpointNames: string[] = data.entity instanceof Device ? data.entity.getEndpointNames() : [];
|
||||
for (let [prop, value] of Object.entries(data.update)) {
|
||||
const endpointNameMatch = utils.endpointNames.find((n) => prop.endsWith(`_${n}`));
|
||||
const endpointNameMatch = endpointNames.find((n) => prop.endsWith(`_${n}`));
|
||||
if (endpointNameMatch) {
|
||||
prop = prop.substring(0, prop.length - endpointNameMatch.length - 1);
|
||||
endpointName = endpointNameMatch;
|
||||
@@ -237,8 +241,8 @@ export default class Groups extends Extension {
|
||||
type = 'remove_all';
|
||||
}
|
||||
|
||||
const parsedEntity = utils.parseEntityID(data.message);
|
||||
resolvedEntityDevice = this.zigbee.resolveEntity(parsedEntity.ID) as Device;
|
||||
const parsedEntity = this.zigbee.resolveEntityAndEndpoint(data.message);
|
||||
resolvedEntityDevice = parsedEntity.entity as Device;
|
||||
if (!resolvedEntityDevice || !(resolvedEntityDevice instanceof Device)) {
|
||||
logger.error(`Device '${data.message}' does not exist`);
|
||||
|
||||
@@ -255,7 +259,12 @@ export default class Groups extends Extension {
|
||||
|
||||
return null;
|
||||
}
|
||||
resolvedEntityEndpoint = resolvedEntityDevice.endpoint(parsedEntity.endpoint);
|
||||
|
||||
resolvedEntityEndpoint = parsedEntity.endpoint;
|
||||
if (parsedEntity.endpointID && !resolvedEntityEndpoint) {
|
||||
logger.error(`Device '${parsedEntity.ID}' does not have endpoint '${parsedEntity.endpointID}'`);
|
||||
return null;
|
||||
}
|
||||
} else if (topicRegexMatch) {
|
||||
type = topicRegexMatch[1] as 'remove' | 'add' | 'remove_all';
|
||||
const message = JSON.parse(data.message);
|
||||
@@ -270,13 +279,16 @@ export default class Groups extends Extension {
|
||||
}
|
||||
}
|
||||
|
||||
const parsed = utils.parseEntityID(message.device);
|
||||
resolvedEntityDevice = this.zigbee.resolveEntity(parsed.ID) as Device;
|
||||
const parsed = this.zigbee.resolveEntityAndEndpoint(message.device);
|
||||
resolvedEntityDevice = parsed?.entity as Device;
|
||||
if (!error && (!resolvedEntityDevice || !(resolvedEntityDevice instanceof Device))) {
|
||||
error = `Device '${message.device}' does not exist`;
|
||||
}
|
||||
if (!error) {
|
||||
resolvedEntityEndpoint = resolvedEntityDevice.endpoint(parsed.endpoint);
|
||||
resolvedEntityEndpoint = parsed.endpoint;
|
||||
if (parsed.endpointID && !resolvedEntityEndpoint) {
|
||||
error = `Device '${parsed.ID}' does not have endpoint '${parsed.endpointID}'`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ export default class HomeAssistant extends Extension {
|
||||
|
||||
override async start(): Promise<void> {
|
||||
if (!settings.get().advanced.cache_state) {
|
||||
logger.warn('In order for Home Assistant integration to work properly set `cache_state: true');
|
||||
logger.warning('In order for Home Assistant integration to work properly set `cache_state: true');
|
||||
}
|
||||
|
||||
this.zigbee2MQTTVersion = (await utils.getZigbee2MQTTVersion(false)).version;
|
||||
@@ -210,7 +210,6 @@ export default class HomeAssistant extends Extension {
|
||||
].filter((c) => c);
|
||||
|
||||
if (colorModes.length) {
|
||||
discoveryEntry.discovery_payload.color_mode = true;
|
||||
discoveryEntry.discovery_payload.supported_color_modes = colorModes;
|
||||
}
|
||||
|
||||
@@ -600,6 +599,7 @@ export default class HomeAssistant extends Extension {
|
||||
discoveryEntries.push(discoveryEntry);
|
||||
} else if (isBinaryExposeFeature(firstExpose)) {
|
||||
const lookup: {[s: string]: KeyValue}= {
|
||||
activity_led_indicator: {icon: 'mdi:led-on'},
|
||||
auto_off: {icon: 'mdi:flash-auto'},
|
||||
battery_low: {entity_category: 'diagnostic', device_class: 'battery'},
|
||||
button_lock: {entity_category: 'config', icon: 'mdi:lock'},
|
||||
@@ -613,7 +613,7 @@ export default class HomeAssistant extends Extension {
|
||||
card: {entity_category: 'config', icon: 'mdi:clipboard-check'},
|
||||
child_lock: {entity_category: 'config', icon: 'mdi:account-lock'},
|
||||
color_sync: {entity_category: 'config', icon: 'mdi:sync-circle'},
|
||||
consumer_connected: {entity_category: 'diagnostic', device_class: 'connectivity'},
|
||||
consumer_connected: {device_class: 'plug'},
|
||||
contact: {device_class: 'door'},
|
||||
garage_door_contact: {device_class: 'garage_door', payload_on: false, payload_off: true},
|
||||
eco_mode: {entity_category: 'config', icon: 'mdi:leaf'},
|
||||
@@ -629,6 +629,7 @@ export default class HomeAssistant extends Extension {
|
||||
motor_reversal: {entity_category: 'config', icon: 'mdi:arrow-left-right'},
|
||||
moving: {device_class: 'moving'},
|
||||
no_position_support: {entity_category: 'config', icon: 'mdi:minus-circle-outline'},
|
||||
noise_detected: {device_class: 'sound'},
|
||||
occupancy: {device_class: 'motion'},
|
||||
power_outage_memory: {entity_category: 'config', icon: 'mdi:memory'},
|
||||
presence: {device_class: 'presence'},
|
||||
@@ -644,6 +645,7 @@ export default class HomeAssistant extends Extension {
|
||||
tamper: {device_class: 'tamper'},
|
||||
temperature_scale: {entity_category: 'config', icon: 'mdi:temperature-celsius'},
|
||||
test: {entity_category: 'diagnostic', icon: 'mdi:test-tube'},
|
||||
th_heater: {icon: 'mdi:heat-wave'},
|
||||
trigger_indicator: {icon: 'mdi:led-on'},
|
||||
valve_alarm: {device_class: 'problem'},
|
||||
valve_detection: {icon: 'mdi:pipe-valve'},
|
||||
@@ -682,9 +684,6 @@ export default class HomeAssistant extends Extension {
|
||||
},
|
||||
};
|
||||
|
||||
// Let Home Assistant generate entity name when device_class is present
|
||||
if (discoveryEntry.discovery_payload.device_class) delete discoveryEntry.discovery_payload.name;
|
||||
|
||||
discoveryEntries.push(discoveryEntry);
|
||||
} else {
|
||||
const discoveryEntry: DiscoveryEntry = {
|
||||
@@ -700,9 +699,6 @@ export default class HomeAssistant extends Extension {
|
||||
},
|
||||
};
|
||||
|
||||
// Let Home Assistant generate entity name when device_class is present
|
||||
if (discoveryEntry.discovery_payload.device_class) delete discoveryEntry.discovery_payload.name;
|
||||
|
||||
discoveryEntries.push(discoveryEntry);
|
||||
}
|
||||
} else if (isNumericExposeFeature(firstExpose)) {
|
||||
@@ -792,6 +788,11 @@ export default class HomeAssistant extends Extension {
|
||||
min_temperature: {entity_category: 'config', icon: 'mdi:thermometer-low'},
|
||||
minimum_on_level: {entity_category: 'config'},
|
||||
measurement_poll_interval: {entity_category: 'config', icon: 'mdi:clock-out'},
|
||||
noise: {device_class: 'sound_pressure', state_class: 'measurement'},
|
||||
noise_detect_level: {icon: 'mdi:volume-equal'},
|
||||
noise_timeout: {icon: 'mdi:timer'},
|
||||
occupancy_level: {icon: 'mdi:motion-sensor'},
|
||||
occupancy_sensitivity: {icon: 'mdi:motion-sensor'},
|
||||
occupancy_timeout: {entity_category: 'config', icon: 'mdi:timer'},
|
||||
overload_protection: {icon: 'mdi:flash'},
|
||||
pm10: {device_class: 'pm10', state_class: 'measurement'},
|
||||
@@ -801,7 +802,7 @@ export default class HomeAssistant extends Extension {
|
||||
power: {device_class: 'power', entity_category: 'diagnostic', state_class: 'measurement'},
|
||||
power_factor: {device_class: 'power_factor', enabled_by_default: false,
|
||||
entity_category: 'diagnostic', state_class: 'measurement'},
|
||||
power_outage_count: {icon: 'mdi:counter'},
|
||||
power_outage_count: {icon: 'mdi:counter', enabled_by_default: false},
|
||||
precision: {entity_category: 'config', icon: 'mdi:decimal-comma-increase'},
|
||||
pressure: {device_class: 'atmospheric_pressure', state_class: 'measurement'},
|
||||
presence_timeout: {entity_category: 'config', icon: 'mdi:timer'},
|
||||
@@ -818,7 +819,9 @@ export default class HomeAssistant extends Extension {
|
||||
temperature_calibration: {entity_category: 'config', icon: 'mdi:wrench-clock'},
|
||||
temperature_max: {entity_category: 'config', icon: 'mdi:thermometer-plus'},
|
||||
temperature_min: {entity_category: 'config', icon: 'mdi:thermometer-minus'},
|
||||
temperature_offset: {icon: 'mdi:thermometer-lines'},
|
||||
transition: {entity_category: 'config', icon: 'mdi:transition'},
|
||||
trigger_count: {icon: 'mdi:counter', enabled_by_default: false},
|
||||
voc: {device_class: 'volatile_organic_compounds', state_class: 'measurement'},
|
||||
voc_index: {state_class: 'measurement'},
|
||||
vibration_timeout: {entity_category: 'config', icon: 'mdi:timer'},
|
||||
@@ -891,9 +894,6 @@ export default class HomeAssistant extends Extension {
|
||||
discoveryEntry.discovery_payload.entity_category = 'diagnostic';
|
||||
}
|
||||
|
||||
// Let Home Assistant generate entity name when device_class is present
|
||||
if (discoveryEntry.discovery_payload.device_class) delete discoveryEntry.discovery_payload.name;
|
||||
|
||||
discoveryEntries.push(discoveryEntry);
|
||||
|
||||
/**
|
||||
@@ -924,9 +924,6 @@ export default class HomeAssistant extends Extension {
|
||||
delete discoveryEntry.discovery_payload.device_class;
|
||||
}
|
||||
|
||||
// Let Home Assistant generate entity name when device_class is present
|
||||
if (discoveryEntry.discovery_payload.device_class) delete discoveryEntry.discovery_payload.name;
|
||||
|
||||
if (firstExpose.value_min != null) discoveryEntry.discovery_payload.min = firstExpose.value_min;
|
||||
if (firstExpose.value_max != null) discoveryEntry.discovery_payload.max = firstExpose.value_max;
|
||||
|
||||
@@ -946,6 +943,7 @@ export default class HomeAssistant extends Extension {
|
||||
effect: {enabled_by_default: false, icon: 'mdi:palette'},
|
||||
force: {entity_category: 'config', icon: 'mdi:valve'},
|
||||
keep_time: {entity_category: 'config', icon: 'mdi:av-timer'},
|
||||
identify: {device_class: 'identify'},
|
||||
keypad_lockout: {entity_category: 'config', icon: 'mdi:lock'},
|
||||
load_detection_mode: {entity_category: 'config', icon: 'mdi:tune'},
|
||||
load_dimmable: {entity_category: 'config', icon: 'mdi:chart-bell-curve'},
|
||||
@@ -960,6 +958,7 @@ export default class HomeAssistant extends Extension {
|
||||
power_outage_memory: {entity_category: 'config', icon: 'mdi:power-settings'},
|
||||
power_supply_mode: {entity_category: 'config', icon: 'mdi:power-settings'},
|
||||
power_type: {entity_category: 'config', icon: 'mdi:lightning-bolt-circle'},
|
||||
restart: {device_class: 'restart'},
|
||||
sensitivity: {entity_category: 'config', icon: 'mdi:tune'},
|
||||
sensor: {icon: 'mdi:tune'},
|
||||
sensors_type: {entity_category: 'config', icon: 'mdi:tune'},
|
||||
@@ -969,6 +968,7 @@ export default class HomeAssistant extends Extension {
|
||||
temperature_display_mode: {entity_category: 'config', icon: 'mdi:thermometer'},
|
||||
temperature_sensor_select: {entity_category: 'config', icon: 'mdi:home-thermometer'},
|
||||
thermostat_unit: {entity_category: 'config', icon: 'mdi:thermometer'},
|
||||
update: {device_class: 'update'},
|
||||
volume: {entity_category: 'config', icon: 'mdi: volume-high'},
|
||||
week: {entity_category: 'config', icon: 'mdi:calendar-clock'},
|
||||
};
|
||||
@@ -1008,6 +1008,29 @@ export default class HomeAssistant extends Extension {
|
||||
command_topic: true,
|
||||
command_topic_postfix: firstExpose.property,
|
||||
options: firstExpose.values.map((v) => v.toString()),
|
||||
enabled_by_default: firstExpose.values.length !== 1, // hide if button is exposed
|
||||
...lookup[firstExpose.name],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* If enum has only item and only supports SET then expose as button entity.
|
||||
* Note: select entity is hidden by default to avoid breaking changes
|
||||
* for selects already existing in HA (legacy).
|
||||
*/
|
||||
if (firstExpose.access & ACCESS_SET && firstExpose.values.length === 1) {
|
||||
discoveryEntries.push({
|
||||
type: 'button',
|
||||
object_id: firstExpose.property,
|
||||
mockProperties: [],
|
||||
discovery_payload: {
|
||||
name: endpoint ? `${firstExpose.label} ${endpoint}` : firstExpose.label,
|
||||
state_topic: false,
|
||||
command_topic_prefix: endpoint,
|
||||
command_topic: true,
|
||||
command_topic_postfix: firstExpose.property,
|
||||
payload_press: firstExpose.values[0].toString(),
|
||||
...lookup[firstExpose.name],
|
||||
},
|
||||
});
|
||||
@@ -1075,6 +1098,13 @@ export default class HomeAssistant extends Extension {
|
||||
}
|
||||
});
|
||||
|
||||
discoveryEntries.forEach((d) => {
|
||||
// Let Home Assistant generate entity name when device_class is present
|
||||
if (d.discovery_payload.device_class) {
|
||||
delete d.discovery_payload.name;
|
||||
}
|
||||
});
|
||||
|
||||
return discoveryEntries;
|
||||
}
|
||||
|
||||
|
||||
@@ -320,7 +320,7 @@ export default class BridgeLegacy extends Extension {
|
||||
try {
|
||||
logger.info(`${lookup[action][1]} '${entity.name}'`);
|
||||
if (action === 'force_remove') {
|
||||
await entity.zh.removeFromDatabase();
|
||||
entity.zh.removeFromDatabase();
|
||||
} else {
|
||||
await entity.zh.removeFromNetwork();
|
||||
}
|
||||
@@ -445,7 +445,7 @@ export default class BridgeLegacy extends Extension {
|
||||
stringify({type: `touchlink`, message: 'reset_success', meta: {status: 'success'}}),
|
||||
);
|
||||
} else {
|
||||
logger.warn('Failed to factory reset device through Touchlink');
|
||||
logger.warning('Failed to factory reset device through Touchlink');
|
||||
this.mqtt.publish(
|
||||
'bridge/log',
|
||||
stringify({type: `touchlink`, message: 'reset_failed', meta: {status: 'failed'}}),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* istanbul ignore file */
|
||||
import * as settings from '../../util/settings';
|
||||
import logger from '../../util/logger';
|
||||
import utils from '../../util/utils';
|
||||
import Extension from '../extension';
|
||||
import bind from 'bind-decorator';
|
||||
import Device from '../../model/device';
|
||||
@@ -19,19 +18,25 @@ export default class DeviceGroupMembership extends Extension {
|
||||
return null;
|
||||
}
|
||||
|
||||
const parsed = utils.parseEntityID(match[1]);
|
||||
const device = this.zigbee.resolveEntity(parsed.ID) as Device;
|
||||
const parsed = this.zigbee.resolveEntityAndEndpoint(match[1]);
|
||||
const device = parsed?.entity as Device;
|
||||
if (!device || !(device instanceof Device)) {
|
||||
logger.error(`Device '${match[1]}' does not exist`);
|
||||
return;
|
||||
}
|
||||
const endpoint = device.endpoint(parsed.endpoint);
|
||||
|
||||
const endpoint = parsed.endpoint;
|
||||
if (parsed.endpointID && !endpoint) {
|
||||
logger.error(`Device '${parsed.ID}' does not have endpoint '${parsed.endpointID}'`);
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await endpoint.command(
|
||||
`genGroups`, 'getMembership', {groupcount: 0, grouplist: []}, {},
|
||||
);
|
||||
|
||||
if (!response) {
|
||||
logger.warn(`Couldn't get group membership of ${device.ieeeAddr}`);
|
||||
logger.warning(`Couldn't get group membership of ${device.ieeeAddr}`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,16 +39,16 @@ export default class SoftReset extends Extension {
|
||||
}
|
||||
|
||||
private async handleTimeout(): Promise<void> {
|
||||
logger.warn('Soft reset timeout triggered');
|
||||
logger.warning('Soft reset timeout triggered');
|
||||
|
||||
try {
|
||||
await this.zigbee.reset('soft');
|
||||
logger.warn('Soft reset ZNP due to timeout');
|
||||
logger.warning('Soft reset ZNP due to timeout');
|
||||
} catch (error) {
|
||||
logger.warn('Soft reset failed, trying stop/start');
|
||||
logger.warning('Soft reset failed, trying stop/start');
|
||||
|
||||
await this.zigbee.stop();
|
||||
logger.warn('Zigbee stopped');
|
||||
logger.warning('Zigbee stopped');
|
||||
|
||||
try {
|
||||
await this.zigbee.start();
|
||||
|
||||
@@ -65,6 +65,10 @@ export default class OTAUpdate extends Extension {
|
||||
// remove them.
|
||||
for (const device of this.zigbee.devices(false)) {
|
||||
this.removeProgressAndRemainingFromState(device);
|
||||
// Reset update state, e.g. when Z2M restarted during update.
|
||||
if (this.state.get(device).update?.state === 'updating') {
|
||||
this.state.get(device).update.state = 'available';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +98,7 @@ export default class OTAUpdate extends Extension {
|
||||
try {
|
||||
// @ts-expect-error typing guaranteed by data.type
|
||||
const dataData: zhc.ota.ImageInfo = data.data;
|
||||
availableResult = await data.device.definition.ota.isUpdateAvailable(data.device.zh, logger, dataData);
|
||||
availableResult = await data.device.definition.ota.isUpdateAvailable(data.device.zh, dataData);
|
||||
} catch (e) {
|
||||
supportsOTA = false;
|
||||
logger.debug(`Failed to check if update available for '${data.device.name}' (${e.message})`);
|
||||
@@ -202,7 +206,7 @@ export default class OTAUpdate extends Extension {
|
||||
}
|
||||
|
||||
try {
|
||||
const availableResult = await device.definition.ota.isUpdateAvailable(device.zh, logger, null);
|
||||
const availableResult = await device.definition.ota.isUpdateAvailable(device.zh, null);
|
||||
const msg = `${availableResult.available ? 'Update' : 'No update'} available for '${device.name}'`;
|
||||
logger.info(msg);
|
||||
|
||||
@@ -266,7 +270,7 @@ export default class OTAUpdate extends Extension {
|
||||
};
|
||||
|
||||
const from_ = await this.readSoftwareBuildIDAndDateCode(device, 'immediate');
|
||||
const fileVersion = await device.definition.ota.updateToLatest(device.zh, logger, onProgress);
|
||||
const fileVersion = await device.definition.ota.updateToLatest(device.zh, onProgress);
|
||||
logger.info(`Finished update of '${device.name}'`);
|
||||
this.eventBus.emitReconfigure({device});
|
||||
this.removeProgressAndRemainingFromState(device);
|
||||
|
||||
+26
-16
@@ -10,8 +10,7 @@ import Group from '../model/group';
|
||||
import Device from '../model/device';
|
||||
import bind from 'bind-decorator';
|
||||
|
||||
const topicRegex = new RegExp(`^(.+?)(?:/(${utils.endpointNames.join('|')}|\\d+))?/(get|set)(?:/(.+))?`);
|
||||
const propertyEndpointRegex = new RegExp(`^(.*?)_(${utils.endpointNames.join('|')})$`);
|
||||
const topicGetSetRegex = new RegExp(`^(.+?)/(get|set)(?:/(.+))?`);
|
||||
const stateValues = ['on', 'off', 'toggle', 'open', 'close', 'stop', 'lock', 'unlock'];
|
||||
const sceneConverterKeys = ['scene_store', 'scene_add', 'scene_remove', 'scene_remove_all', 'scene_rename'];
|
||||
|
||||
@@ -40,18 +39,30 @@ export default class Publish extends Extension {
|
||||
}
|
||||
|
||||
parseTopic(topic: string): ParsedTopic | null {
|
||||
const match = topic.match(topicRegex);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
// The function supports the following topic formats (below are for 'set'. 'get' will look the same):
|
||||
// - <base_topic>/device_name/set (endpoint and attribute is defined in the payload)
|
||||
// - <base_topic>/device_name/set/attribute (default endpoint used)
|
||||
// - <base_topic>/device_name/endpoint/set (attribute is defined in the payload)
|
||||
// - <base_topic>/device_name/endpoint/set/attribute (payload is the value)
|
||||
|
||||
const ID = match[1].replace(`${settings.get().mqtt.base_topic}/`, '');
|
||||
// If we didn't replace base_topic we received something we don't care about
|
||||
if (ID === match[1] || ID.match(/bridge/)) {
|
||||
return null;
|
||||
}
|
||||
// The first step is to get rid of base topic part
|
||||
topic = topic.replace(`${settings.get().mqtt.base_topic}/`, '');
|
||||
|
||||
return {ID: ID, endpoint: match[2], type: match[3] as 'get' | 'set', attribute: match[4]};
|
||||
// Also bridge requests are something we don't care about
|
||||
if (topic.match(/bridge/)) return null;
|
||||
|
||||
// Make the rough split on get/set keyword.
|
||||
// Before the get/set is the device name and optional endpoint name.
|
||||
// After it there will be an optional attribute name.
|
||||
const match = topic.match(topicGetSetRegex);
|
||||
if (!match) return null;
|
||||
|
||||
const deviceNameAndEndpoint = match[1];
|
||||
const attribute = match[3];
|
||||
|
||||
// Now parse the device/group name, and endpoint name
|
||||
const entity = this.zigbee.resolveEntityAndEndpoint(deviceNameAndEndpoint);
|
||||
return {ID: entity.ID, endpoint: entity.endpointID, type: match[2] as 'get' | 'set', attribute: attribute};
|
||||
}
|
||||
|
||||
parseMessage(parsedTopic: ParsedTopic, data: eventdata.MQTTMessage): KeyValue | null {
|
||||
@@ -185,6 +196,9 @@ export default class Publish extends Extension {
|
||||
toPublish[ID] = {...toPublish[ID], ...payload};
|
||||
};
|
||||
|
||||
const endpointNames = re instanceof Device ? re.getEndpointNames() : [];
|
||||
const propertyEndpointRegex = new RegExp(`^(.*?)_(${endpointNames.join('|')})$`);
|
||||
|
||||
for (let [key, value] of entries) {
|
||||
let endpointName = parsedTopic.endpoint;
|
||||
let localTarget = target;
|
||||
@@ -196,10 +210,6 @@ export default class Publish extends Extension {
|
||||
endpointName = propertyEndpointMatch[2];
|
||||
key = propertyEndpointMatch[1];
|
||||
localTarget = re.endpoint(endpointName);
|
||||
if (localTarget == null) {
|
||||
logger.error(`Device '${re.name}' has no endpoint '${endpointName}'`);
|
||||
continue;
|
||||
}
|
||||
endpointOrGroupID = localTarget.ID;
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ export default class Receive extends Extension {
|
||||
// for e.g. click switches where we need to count number of clicks and detect long presses.
|
||||
const publish = (payload: KeyValue): void => {
|
||||
const options: KeyValue = data.device.options;
|
||||
zhc.postProcessConvertedFromZigbeeMessage(data.device.definition, payload, options, logger);
|
||||
zhc.postProcessConvertedFromZigbeeMessage(data.device.definition, payload, options);
|
||||
|
||||
if (settings.get().advanced.elapsed) {
|
||||
const now = Date.now();
|
||||
|
||||
+12
-3
@@ -65,12 +65,21 @@ export default class Device {
|
||||
}
|
||||
|
||||
endpointName(endpoint: zh.Endpoint): string {
|
||||
let name = null;
|
||||
let epName = null;
|
||||
if (this.definition?.endpoint) {
|
||||
name = Object.entries(this.definition?.endpoint(this.zh)).find((e) => e[1] == endpoint.ID)[0];
|
||||
const mapping = this.definition?.endpoint(this.zh);
|
||||
for (const [name, id] of Object.entries(mapping)) {
|
||||
if (id == endpoint.ID) {
|
||||
epName = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* istanbul ignore next */
|
||||
return name === 'default' ? null : name;
|
||||
return epName === 'default' ? null : epName;
|
||||
}
|
||||
|
||||
getEndpointNames(): string[] {
|
||||
return Object.keys(this.definition?.endpoint?.(this.zh) ?? {}).filter((name) => name !== 'default');
|
||||
}
|
||||
|
||||
isIkeaTradfri(): boolean {return this.zh.manufacturerID === 4476;}
|
||||
|
||||
+3
-2
@@ -127,7 +127,7 @@ export default class MQTT {
|
||||
{retain: true, qos: 0});
|
||||
this.eventBus.removeListeners(this);
|
||||
logger.info('Disconnecting from MQTT server');
|
||||
this.client.end();
|
||||
this.client?.end();
|
||||
}
|
||||
|
||||
subscribe(topic: string): void {
|
||||
@@ -173,6 +173,7 @@ export default class MQTT {
|
||||
this.eventBus.emitMQTTMessagePublished({topic, payload, options: {...defaultOptions, ...options}});
|
||||
|
||||
if (!this.isConnected()) {
|
||||
/* istanbul ignore else */
|
||||
if (!skipLog) {
|
||||
logger.error(`Not connected to MQTT server!`);
|
||||
logger.error(`Cannot send message: topic: '${topic}', payload: '${payload}`);
|
||||
@@ -181,7 +182,7 @@ export default class MQTT {
|
||||
}
|
||||
|
||||
if (!skipLog) {
|
||||
logger.info(`MQTT publish: topic '${topic}', payload '${payload}'`);
|
||||
logger.debug(`MQTT publish: topic '${topic}', payload '${payload}'`);
|
||||
}
|
||||
|
||||
const actualOptions: mqtt.IClientPublishOptions = {...defaultOptions, ...options};
|
||||
|
||||
Vendored
+1
-1
@@ -143,7 +143,7 @@ declare global {
|
||||
serial: {
|
||||
disable_led: boolean,
|
||||
port?: string,
|
||||
adapter?: 'deconz' | 'zstack' | 'ezsp' | 'zigate',
|
||||
adapter?: 'deconz' | 'zstack' | 'ezsp' | 'zigate' | 'ember',
|
||||
baudrate?: number,
|
||||
rtscts?: boolean,
|
||||
},
|
||||
|
||||
+189
-199
@@ -7,219 +7,209 @@ import fx from 'mkdir-recursive';
|
||||
import {rimrafSync} from 'rimraf';
|
||||
import assert from 'assert';
|
||||
|
||||
const colorizer = winston.format.colorize();
|
||||
const LOG_LEVELS = ['error', 'warning', 'info', 'debug'] as const;
|
||||
type LogLevel = typeof LOG_LEVELS[number];
|
||||
|
||||
type Z2MLogLevel = 'warn' | 'debug' | 'info' | 'error';
|
||||
type WinstonLogLevel = 'warning' | 'debug' | 'info' | 'error';
|
||||
class Logger {
|
||||
private level: LogLevel;
|
||||
private readonly output: string[];
|
||||
private readonly directory: string;
|
||||
private readonly logger: winston.Logger;
|
||||
private readonly fileTransport: winston.transports.FileTransportInstance;
|
||||
|
||||
const z2mToWinstonLevel = (level: Z2MLogLevel): WinstonLogLevel => level === 'warn' ? 'warning' : level;
|
||||
const winstonToZ2mLevel = (level: WinstonLogLevel): Z2MLogLevel => level === 'warning' ? 'warn' : level;
|
||||
constructor() {
|
||||
// What transports to enable
|
||||
this.output = settings.get().advanced.log_output;
|
||||
// Directory to log to
|
||||
const timestamp = moment(Date.now()).format('YYYY-MM-DD.HH-mm-ss');
|
||||
this.directory = settings.get().advanced.log_directory.replace('%TIMESTAMP%', timestamp);
|
||||
const logFilename = settings.get().advanced.log_file.replace('%TIMESTAMP%', timestamp);
|
||||
// Determine the log level.
|
||||
const settingLevel = settings.get().advanced.log_level;
|
||||
// workaround for syslog<>npm level conflict
|
||||
this.level = settingLevel === 'warn' ? 'warning' : settingLevel;
|
||||
|
||||
const levelWithCompensatedLength: {[s: string]: string} = {
|
||||
'info': 'info ',
|
||||
'error': 'error',
|
||||
'warn': 'warn ',
|
||||
'debug': 'debug',
|
||||
};
|
||||
assert(
|
||||
LOG_LEVELS.includes(this.level),
|
||||
`'${this.level}' is not valid log_level, use one of '${LOG_LEVELS.join(', ')}'`,
|
||||
);
|
||||
|
||||
let logger: winston.Logger;
|
||||
let fileTransport : winston.transport;
|
||||
let output: string[];
|
||||
let directory: string;
|
||||
let logFilename: string;
|
||||
let transportsToUse: winston.transport[];
|
||||
const timestampFormat = (): string => moment().format(settings.get().advanced.timestamp_format);
|
||||
|
||||
function init(): void {
|
||||
// What transports to enable
|
||||
output = settings.get().advanced.log_output;
|
||||
|
||||
// Directory to log to
|
||||
const timestamp = moment(Date.now()).format('YYYY-MM-DD.HH-mm-ss');
|
||||
directory = settings.get().advanced.log_directory.replace('%TIMESTAMP%', timestamp);
|
||||
logFilename = settings.get().advanced.log_file.replace('%TIMESTAMP%', timestamp);
|
||||
|
||||
// Make sure that log directory exists when not logging to stdout only
|
||||
if (output.includes('file')) {
|
||||
fx.mkdirSync(directory);
|
||||
|
||||
if (settings.get().advanced.log_symlink_current) {
|
||||
const current = settings.get().advanced.log_directory.replace('%TIMESTAMP%', 'current');
|
||||
const actual = './' + timestamp;
|
||||
/* istanbul ignore next */
|
||||
if (fs.existsSync(current)) {
|
||||
fs.unlinkSync(current);
|
||||
}
|
||||
fs.symlinkSync(actual, current);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the log level.
|
||||
const z2mLevel = settings.get().advanced.log_level;
|
||||
const validLevels = ['info', 'error', 'warn', 'debug'];
|
||||
assert(validLevels.includes(z2mLevel),
|
||||
`'${z2mLevel}' is not valid log_level, use one of '${validLevels.join(', ')}'`);
|
||||
const level = z2mToWinstonLevel(z2mLevel);
|
||||
|
||||
const timestampFormat = (): string => moment().format(settings.get().advanced.timestamp_format);
|
||||
|
||||
// Setup default console logger
|
||||
transportsToUse = [
|
||||
new winston.transports.Console({
|
||||
level,
|
||||
silent: !output.includes('console'),
|
||||
this.logger = winston.createLogger({
|
||||
level: this.level,
|
||||
format: winston.format.combine(
|
||||
winston.format.errors({stack: true}),
|
||||
winston.format.timestamp({format: timestampFormat}),
|
||||
winston.format.printf(/* istanbul ignore next */(info) => {
|
||||
const {timestamp, level, message} = info;
|
||||
const l = winstonToZ2mLevel(level as WinstonLogLevel);
|
||||
),
|
||||
levels: winston.config.syslog.levels,
|
||||
});
|
||||
|
||||
const plainPrefix = `Zigbee2MQTT:${levelWithCompensatedLength[l]}`;
|
||||
let prefix = plainPrefix;
|
||||
if (process.stdout.isTTY) {
|
||||
prefix = colorizer.colorize(l, plainPrefix);
|
||||
}
|
||||
return `${prefix} ${timestamp.split('.')[0]}: ${message}`;
|
||||
const consoleSilenced = !this.output.includes('console');
|
||||
// Print to user what logging is active
|
||||
let logging = `Logging to console${consoleSilenced ? ' (silenced)' : ''}`;
|
||||
|
||||
// Setup default console logger
|
||||
this.logger.add(new winston.transports.Console({
|
||||
silent: consoleSilenced,
|
||||
// winston.config.syslog.levels sets 'warning' as 'red'
|
||||
format: winston.format.combine(
|
||||
winston.format.colorize({colors: {debug: 'blue', info: 'green', warning: 'yellow', error: 'red'}}),
|
||||
winston.format.printf(/* istanbul ignore next */(info) => {
|
||||
return `[${info.timestamp}] ${info.level}: \t${info.namespace}: ${info.message}`;
|
||||
}),
|
||||
),
|
||||
}),
|
||||
];
|
||||
}));
|
||||
|
||||
// Add file logger when enabled
|
||||
// NOTE: the initiation of the logger, even when not added as transport tries to create the logging directory
|
||||
const transportFileOptions: KeyValue = {
|
||||
filename: path.join(directory, logFilename),
|
||||
json: false,
|
||||
level,
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp({format: timestampFormat}),
|
||||
winston.format.printf(/* istanbul ignore next */(info) => {
|
||||
const {timestamp, level, message} = info;
|
||||
const l = winstonToZ2mLevel(level as WinstonLogLevel);
|
||||
return `${levelWithCompensatedLength[l]} ${timestamp.split('.')[0]}: ${message}`;
|
||||
}),
|
||||
),
|
||||
};
|
||||
if (this.output.includes('file')) {
|
||||
logging += `, file (filename: ${logFilename})`;
|
||||
|
||||
if (settings.get().advanced.log_rotation) {
|
||||
transportFileOptions.tailable = true;
|
||||
transportFileOptions.maxFiles = 3; // Keep last 3 files
|
||||
transportFileOptions.maxsize = 10000000; // 10MB
|
||||
}
|
||||
// Make sure that log directory exists when not logging to stdout only
|
||||
fx.mkdirSync(this.directory);
|
||||
|
||||
if (output.includes('file')) {
|
||||
fileTransport = new winston.transports.File(transportFileOptions);
|
||||
transportsToUse.push(fileTransport);
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (output.includes('syslog')) {
|
||||
// eslint-disable-next-line
|
||||
require('winston-syslog').Syslog;
|
||||
const options: KeyValue = {
|
||||
app_name: 'Zigbee2MQTT',
|
||||
format: winston.format.printf(/* istanbul ignore next */(info) => {
|
||||
return `${info.message}`;
|
||||
}),
|
||||
...settings.get().advanced.log_syslog,
|
||||
};
|
||||
if (options.hasOwnProperty('type')) options.type = options.type.toString();
|
||||
// @ts-ignore
|
||||
transportsToUse.push(new winston.transports.Syslog(options));
|
||||
}
|
||||
|
||||
logger = winston.createLogger({transports: transportsToUse, levels: winston.config.syslog.levels});
|
||||
}
|
||||
|
||||
// Cleanup any old log directory.
|
||||
function cleanup(): void {
|
||||
if (settings.get().advanced.log_directory.includes('%TIMESTAMP%')) {
|
||||
const rootDirectory = path.join(directory, '..');
|
||||
|
||||
let directories = fs.readdirSync(rootDirectory).map((d) => {
|
||||
d = path.join(rootDirectory, d);
|
||||
return {path: d, birth: fs.statSync(d).mtime};
|
||||
});
|
||||
|
||||
directories.sort((a: KeyValue, b: KeyValue) => b.birth - a.birth);
|
||||
directories = directories.slice(10, directories.length);
|
||||
directories.forEach((dir) => {
|
||||
logger.debug(`Removing old log directory '${dir.path}'`);
|
||||
rimrafSync(dir.path);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Print to user what logging is enabled
|
||||
function logOutput(): void {
|
||||
if (output.includes('file')) {
|
||||
if (output.includes('console')) {
|
||||
logger.info(`Logging to console and directory: '${directory}' filename: ${logFilename}`);
|
||||
} else {
|
||||
logger.info(`Logging to directory: '${directory}' filename: ${logFilename}`);
|
||||
}
|
||||
cleanup();
|
||||
} else if (output.includes('console')) {
|
||||
logger.info(`Logging to console only'`);
|
||||
}
|
||||
}
|
||||
|
||||
function addTransport(transport: winston.transport): void {
|
||||
transport.level = transportsToUse[0].level;
|
||||
logger.add(transport);
|
||||
}
|
||||
|
||||
function getLevel(): Z2MLogLevel {
|
||||
return winstonToZ2mLevel(transportsToUse[0].level as WinstonLogLevel);
|
||||
}
|
||||
|
||||
function setLevel(level: Z2MLogLevel): void {
|
||||
logger.transports.forEach((transport) => transport.level = z2mToWinstonLevel(level as Z2MLogLevel));
|
||||
}
|
||||
|
||||
function warn(message: string): void {
|
||||
// winston.config.syslog.levels doesn't have warn, but is required for syslog.
|
||||
logger.warning(message);
|
||||
}
|
||||
|
||||
function warning(message: string): void {
|
||||
logger.warning(message);
|
||||
}
|
||||
|
||||
function info(message: string): void {
|
||||
logger.info(message);
|
||||
}
|
||||
|
||||
function debug(message: string): void {
|
||||
logger.debug(message);
|
||||
}
|
||||
|
||||
function error(message: string): void {
|
||||
logger.error(message);
|
||||
}
|
||||
|
||||
// Workaround for https://github.com/winstonjs/winston/issues/1629.
|
||||
// https://github.com/Koenkk/zigbee2mqtt/pull/10905
|
||||
/* istanbul ignore next */
|
||||
async function end(): Promise<void> {
|
||||
logger.end();
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
if (!fileTransport) {
|
||||
process.nextTick(resolve);
|
||||
} else {
|
||||
// @ts-ignore
|
||||
if (fileTransport._dest) {
|
||||
// @ts-ignore
|
||||
fileTransport._dest.on('finish', resolve);
|
||||
} else {
|
||||
// @ts-ignore
|
||||
fileTransport.on('open', () => fileTransport._dest.on('finish', resolve));
|
||||
if (settings.get().advanced.log_symlink_current) {
|
||||
const current = settings.get().advanced.log_directory.replace('%TIMESTAMP%', 'current');
|
||||
const actual = './' + timestamp;
|
||||
/* istanbul ignore next */
|
||||
if (fs.existsSync(current)) {
|
||||
fs.unlinkSync(current);
|
||||
}
|
||||
fs.symlinkSync(actual, current);
|
||||
}
|
||||
|
||||
// Add file logger when enabled
|
||||
// eslint-disable-next-line max-len
|
||||
// NOTE: the initiation of the logger even when not added as transport tries to create the logging directory
|
||||
const transportFileOptions: KeyValue = {
|
||||
filename: path.join(this.directory, logFilename),
|
||||
json: false,
|
||||
format: winston.format.printf(/* istanbul ignore next */(info) => {
|
||||
return `[${info.timestamp}] ${info.level}: \t${info.namespace}: ${info.message}`;
|
||||
}),
|
||||
};
|
||||
|
||||
if (settings.get().advanced.log_rotation) {
|
||||
transportFileOptions.tailable = true;
|
||||
transportFileOptions.maxFiles = 3; // Keep last 3 files
|
||||
transportFileOptions.maxsize = 10000000; // 10MB
|
||||
}
|
||||
|
||||
this.fileTransport = new winston.transports.File(transportFileOptions);
|
||||
this.logger.add(this.fileTransport);
|
||||
}
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (this.output.includes('syslog')) {
|
||||
logging += `, syslog`;
|
||||
// eslint-disable-next-line
|
||||
require('winston-syslog').Syslog;
|
||||
|
||||
const options: KeyValue = {
|
||||
app_name: 'Zigbee2MQTT',
|
||||
format: winston.format.printf(/* istanbul ignore next */(info) => {
|
||||
return `${info.namespace}: ${info.message}`;
|
||||
}),
|
||||
...settings.get().advanced.log_syslog,
|
||||
};
|
||||
|
||||
if (options.hasOwnProperty('type')) {
|
||||
options.type = options.type.toString();
|
||||
}
|
||||
|
||||
// @ts-expect-error untyped transport
|
||||
this.logger.add(new winston.transports.Syslog(options));
|
||||
}
|
||||
|
||||
this.info(logging);
|
||||
}
|
||||
|
||||
get winston(): winston.Logger {
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
public addTransport(transport: winston.transport): void {
|
||||
transport.level = this.level;
|
||||
this.logger.add(transport);
|
||||
}
|
||||
|
||||
public removeTransport(transport: winston.transport): void {
|
||||
this.logger.remove(transport);
|
||||
}
|
||||
|
||||
// TODO refactor Z2M level to 'warning' to simplify logic
|
||||
public getLevel(): LogLevel | 'warn' {
|
||||
return this.level === 'warning' ? 'warn' : this.level;
|
||||
}
|
||||
|
||||
public setLevel(level: LogLevel | 'warn'): void {
|
||||
if (level === 'warn') {
|
||||
level = 'warning';
|
||||
}
|
||||
|
||||
this.level = level;
|
||||
this.logger.transports.forEach((transport) => transport.level = this.level);
|
||||
}
|
||||
|
||||
public warning(message: string, namespace: string = 'z2m'): void {
|
||||
this.logger.warning(message, {namespace});
|
||||
}
|
||||
|
||||
public info(message: string, namespace: string = 'z2m'): void {
|
||||
this.logger.info(message, {namespace});
|
||||
}
|
||||
|
||||
public debug(message: string, namespace: string = 'z2m'): void {
|
||||
if (this.level !== 'debug') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.debug(message, {namespace});
|
||||
}
|
||||
|
||||
public error(message: string, namespace: string = 'z2m'): void {
|
||||
this.logger.error(message, {namespace});
|
||||
}
|
||||
|
||||
// Cleanup any old log directory.
|
||||
public cleanup(): void {
|
||||
if (settings.get().advanced.log_directory.includes('%TIMESTAMP%')) {
|
||||
const rootDirectory = path.join(this.directory, '..');
|
||||
|
||||
let directories = fs.readdirSync(rootDirectory).map((d) => {
|
||||
d = path.join(rootDirectory, d);
|
||||
return {path: d, birth: fs.statSync(d).mtime};
|
||||
});
|
||||
|
||||
directories.sort((a: KeyValue, b: KeyValue) => b.birth - a.birth);
|
||||
directories = directories.slice(10, directories.length);
|
||||
directories.forEach((dir) => {
|
||||
this.debug(`Removing old log directory '${dir.path}'`);
|
||||
rimrafSync(dir.path);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround for https://github.com/winstonjs/winston/issues/1629.
|
||||
// https://github.com/Koenkk/zigbee2mqtt/pull/10905
|
||||
/* istanbul ignore next */
|
||||
public async end(): Promise<void> {
|
||||
this.logger.end();
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
if (!this.fileTransport) {
|
||||
process.nextTick(resolve);
|
||||
} else {
|
||||
// @ts-expect-error workaround
|
||||
if (this.fileTransport._dest) {
|
||||
// @ts-expect-error workaround
|
||||
this.fileTransport._dest.on('finish', resolve);
|
||||
} else {
|
||||
// @ts-expect-error workaround
|
||||
this.fileTransport.on('open', () => this.fileTransport._dest.on('finish', resolve));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
init, logOutput, warn, warning, error, info, debug, setLevel, getLevel, cleanup, addTransport, end,
|
||||
winston: (): winston.Logger => logger,
|
||||
};
|
||||
export default new Logger();
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
},
|
||||
"adapter": {
|
||||
"type": ["string"],
|
||||
"enum": ["deconz", "zstack", "zigate", "ezsp", "auto"],
|
||||
"enum": ["deconz", "zstack", "zigate", "ezsp", "auto", "ember"],
|
||||
"title": "Adapter",
|
||||
"default": "auto",
|
||||
"requiresRestart": true,
|
||||
|
||||
@@ -80,7 +80,7 @@ const defaults: RecursivePartial<Settings> = {
|
||||
log_symlink_current: false,
|
||||
log_output: ['console', 'file'],
|
||||
log_directory: path.join(data.getPath(), 'log', '%TIMESTAMP%'),
|
||||
log_file: 'log.txt',
|
||||
log_file: 'log.log',
|
||||
log_level: /* istanbul ignore next */ process.env.DEBUG ? 'debug' : 'info',
|
||||
log_syslog: {},
|
||||
pan_id: 0x1a62,
|
||||
@@ -593,7 +593,7 @@ export function removeDevice(IDorName: string): void {
|
||||
// Remove device from groups
|
||||
if (settings.groups) {
|
||||
const regex =
|
||||
new RegExp(`^(${device.friendly_name}|${device.ID})(/(\\d|${utils.endpointNames.join('|')}))?$`);
|
||||
new RegExp(`^(${device.friendly_name}|${device.ID})(/[^/]+)?$`);
|
||||
for (const group of Object.values(settings.groups).filter((g) => g.devices)) {
|
||||
group.devices = group.devices.filter((device) => !device.match(regex));
|
||||
}
|
||||
|
||||
+2
-30
@@ -30,22 +30,6 @@ function toLocalISOString(date: Date): string {
|
||||
':' + pad(tzOffset % 60);
|
||||
}
|
||||
|
||||
const endpointNames = [
|
||||
'left', 'right', 'center', 'bottom_left', 'bottom_right', 'default',
|
||||
'top_left', 'top_right', 'white', 'rgb', 'cct', 'system', 'top', 'bottom', 'center_left', 'center_right',
|
||||
'ep1', 'ep2', 'row_1', 'row_2', 'row_3', 'row_4', 'relay', 'usb',
|
||||
'l1', 'l2', 'l3', 'l4', 'l5', 'l6', 'l7', 'l8',
|
||||
'l9', 'l10', 'l11', 'l12', 'l13', 'l14', 'l15', 'l16',
|
||||
'l17', 'l18', 'l19', 'l20', 'l21', 'l22', 'l23', 'l24',
|
||||
'th1', 'th2', 'th3', 'th4', 'th5', 'th6', 'th7', 'th8', 'th9', 'th10',
|
||||
'button_1', 'button_2', 'button_3', 'button_4', 'button_5',
|
||||
'button_6', 'button_7', 'button_8', 'button_9', 'button_10',
|
||||
'button_11', 'button_12', 'button_13', 'button_14', 'button_15',
|
||||
'button_16', 'button_17', 'button_18', 'button_19', 'button_20',
|
||||
'button_light', 'button_fan_high', 'button_fan_med', 'button_fan_low',
|
||||
'heat', 'cool', 'water', 'meter', 'wifi', 'no_occupancy_since',
|
||||
];
|
||||
|
||||
function capitalize(s: string): string {
|
||||
return s[0].toUpperCase() + s.slice(1);
|
||||
}
|
||||
@@ -252,16 +236,10 @@ function getAllFiles(path_: string): string[] {
|
||||
|
||||
function validateFriendlyName(name: string, throwFirstError=false): string[] {
|
||||
const errors = [];
|
||||
for (const endpointName of endpointNames) {
|
||||
if (name.toLowerCase().endsWith('/' + endpointName)) {
|
||||
errors.push(`friendly_name is not allowed to end with: '/${endpointName}'`);
|
||||
}
|
||||
}
|
||||
|
||||
if (name.length === 0) errors.push(`friendly_name must be at least 1 char long`);
|
||||
if (name.endsWith('/') || name.startsWith('/')) errors.push(`friendly_name is not allowed to end or start with /`);
|
||||
if (containsControlCharacter(name)) errors.push(`friendly_name is not allowed to contain control char`);
|
||||
if (endpointNames.includes(name)) errors.push(`Following friendly_name are not allowed: '${endpointNames}'`);
|
||||
if (name.match(/.*\/\d*$/)) errors.push(`Friendly name cannot end with a "/DIGIT" ('${name}')`);
|
||||
if (name.includes('#') || name.includes('+')) {
|
||||
errors.push(`MQTT wildcard (+ and #) not allowed in friendly_name ('${name}')`);
|
||||
@@ -309,12 +287,6 @@ function isAvailabilityEnabledForEntity(entity: Device | Group, settings: Settin
|
||||
return !blocklist.includes(entity.name) && !blocklist.includes(entity.ieeeAddr);
|
||||
}
|
||||
|
||||
const entityIDRegex = new RegExp(`^(.+?)(?:/(${endpointNames.join('|')}|\\d+))?$`);
|
||||
function parseEntityID(ID: string): {ID: string, endpoint: string} {
|
||||
const match = ID.match(entityIDRegex);
|
||||
return match && {ID: match[1], endpoint: match[2]};
|
||||
}
|
||||
|
||||
function isEndpoint(obj: unknown): obj is zh.Endpoint {
|
||||
return obj.constructor.name.toLowerCase() === 'endpoint';
|
||||
}
|
||||
@@ -433,10 +405,10 @@ function getScenes(entity: zh.Endpoint | zh.Group): Scene[] {
|
||||
}
|
||||
|
||||
export default {
|
||||
endpointNames, capitalize, getZigbee2MQTTVersion, getDependencyVersion, formatDate, objectHasProperties,
|
||||
capitalize, getZigbee2MQTTVersion, getDependencyVersion, formatDate, objectHasProperties,
|
||||
equalsPartial, getObjectProperty, getResponse, parseJSON, loadModuleFromText, loadModuleFromFile,
|
||||
removeNullPropertiesFromObject, toNetworkAddressHex, toSnakeCase,
|
||||
parseEntityID, isEndpoint, isZHGroup, hours, minutes, seconds, validateFriendlyName, sleep,
|
||||
isEndpoint, isZHGroup, hours, minutes, seconds, validateFriendlyName, sleep,
|
||||
sanitizeImageParameter, isAvailabilityEnabledForEntity, publishLastSeen, availabilityPayload,
|
||||
getAllFiles, filterProperties, flatten, arrayUnique, clone, computeSettingsToChange, getScenes,
|
||||
};
|
||||
|
||||
+41
-7
@@ -11,6 +11,8 @@ import * as ZHEvents from 'zigbee-herdsman/dist/controller/events';
|
||||
import bind from 'bind-decorator';
|
||||
import {randomInt} from 'crypto';
|
||||
|
||||
const entityIDRegex = new RegExp(`^(.+?)(?:/([^/]+))?$`);
|
||||
|
||||
export default class Zigbee {
|
||||
private herdsman: Controller;
|
||||
private eventBus: EventBus;
|
||||
@@ -56,7 +58,7 @@ export default class Zigbee {
|
||||
|
||||
let startResult;
|
||||
try {
|
||||
this.herdsman = new Controller(herdsmanSettings, logger);
|
||||
this.herdsman = new Controller(herdsmanSettings);
|
||||
startResult = await this.herdsman.start();
|
||||
} catch (error) {
|
||||
logger.error(`Error while starting zigbee-herdsman`);
|
||||
@@ -86,22 +88,22 @@ export default class Zigbee {
|
||||
});
|
||||
this.herdsman.on('deviceInterview', async (data: ZHEvents.DeviceInterviewPayload) => {
|
||||
const device = this.resolveDevice(data.device.ieeeAddr);
|
||||
await device.resolveDefinition();
|
||||
/* istanbul ignore if */ if (!device) return; // Prevent potential race
|
||||
await device.resolveDefinition();
|
||||
const d = {device, status: data.status};
|
||||
this.logDeviceInterview(d);
|
||||
this.eventBus.emitDeviceInterview(d);
|
||||
});
|
||||
this.herdsman.on('deviceJoined', async (data: ZHEvents.DeviceJoinedPayload) => {
|
||||
const device = this.resolveDevice(data.device.ieeeAddr);
|
||||
await device.resolveDefinition();
|
||||
/* istanbul ignore if */ if (!device) return; // Prevent potential race
|
||||
await device.resolveDefinition();
|
||||
logger.info(`Device '${device.name}' joined`);
|
||||
this.eventBus.emitDeviceJoined({device});
|
||||
});
|
||||
this.herdsman.on('deviceLeave', (data: ZHEvents.DeviceLeavePayload) => {
|
||||
const name = settings.getDevice(data.ieeeAddr)?.friendly_name || data.ieeeAddr;
|
||||
logger.warn(`Device '${name}' left the network`);
|
||||
logger.warning(`Device '${name}' left the network`);
|
||||
this.eventBus.emitDeviceLeave({ieeeAddr: data.ieeeAddr, name});
|
||||
});
|
||||
this.herdsman.on('message', async (data: ZHEvents.MessagePayload) => {
|
||||
@@ -132,11 +134,11 @@ export default class Zigbee {
|
||||
};
|
||||
if (passlist.length > 0) {
|
||||
if (!passlist.includes(device.ieeeAddr)) {
|
||||
logger.warn(`Device which is not on passlist connected (${device.ieeeAddr}), removing...`);
|
||||
logger.warning(`Device which is not on passlist connected (${device.ieeeAddr}), removing...`);
|
||||
await remove(device);
|
||||
}
|
||||
} else if (blocklist.includes(device.ieeeAddr)) {
|
||||
logger.warn(`Device on blocklist is connected (${device.ieeeAddr}), removing...`);
|
||||
logger.warning(`Device on blocklist is connected (${device.ieeeAddr}), removing...`);
|
||||
await remove(device);
|
||||
}
|
||||
}
|
||||
@@ -160,7 +162,7 @@ export default class Zigbee {
|
||||
const {vendor, description, model} = data.device.definition;
|
||||
logger.info(`Device '${name}' is supported, identified as: ${vendor} ${description} (${model})`);
|
||||
} else {
|
||||
logger.warn(`Device '${name}' with Zigbee model '${data.device.zh.modelID}' and manufacturer name ` +
|
||||
logger.warning(`Device '${name}' with Zigbee model '${data.device.zh.modelID}' and manufacturer name ` +
|
||||
`'${data.device.zh.manufacturerName}' is NOT supported, ` +
|
||||
// eslint-disable-next-line max-len
|
||||
`please follow https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html`);
|
||||
@@ -283,6 +285,38 @@ export default class Zigbee {
|
||||
}
|
||||
}
|
||||
|
||||
resolveEntityAndEndpoint(ID: string)
|
||||
: {ID: string, entity: Device | Group, endpointID: string, endpoint: zh.Endpoint} {
|
||||
// This function matches the following entity formats:
|
||||
// device_name (just device name)
|
||||
// device_name/ep_name (device name and endpoint numeric ID or name)
|
||||
// device/name (device name with slashes)
|
||||
// device/name/ep_name (device name with slashes, and endpoint numeric ID or name)
|
||||
|
||||
// The function tries to find an exact match first
|
||||
let entityName = ID;
|
||||
let deviceOrGroup = this.resolveEntity(ID);
|
||||
let endpointNameOrID = undefined;
|
||||
|
||||
// If exact match did not happenc, try matching a device_name/endpoint pattern
|
||||
if (!deviceOrGroup) {
|
||||
// First split the input token by the latest slash
|
||||
const match = ID.match(entityIDRegex);
|
||||
|
||||
// Get the resulting IDs from the match
|
||||
entityName = match[1];
|
||||
deviceOrGroup = this.resolveEntity(match[1]);
|
||||
endpointNameOrID = match[2];
|
||||
}
|
||||
|
||||
// If the function returns non-null endpoint name, but the endpoint field is null, then
|
||||
// it means that endpoint was not matched because there is no such endpoint on the device
|
||||
// (or the entity is a group)
|
||||
const endpoint = deviceOrGroup?.isDevice() ? deviceOrGroup.endpoint(endpointNameOrID) : null;
|
||||
|
||||
return {ID: entityName, entity: deviceOrGroup, endpointID: endpointNameOrID, endpoint: endpoint};
|
||||
}
|
||||
|
||||
firstCoordinatorEndpoint(): zh.Endpoint {
|
||||
return this.herdsman.getDevicesByType('Coordinator')[0].endpoints[0];
|
||||
}
|
||||
|
||||
Generated
+955
-988
File diff suppressed because it is too large
Load Diff
+24
-25
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "zigbee2mqtt",
|
||||
"version": "1.35.2",
|
||||
"version": "1.37.0",
|
||||
"description": "Zigbee to MQTT bridge using Zigbee-herdsman",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -43,50 +43,49 @@
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"finalhandler": "^1.2.0",
|
||||
"git-last-commit": "^1.0.1",
|
||||
"humanize-duration": "^3.31.0",
|
||||
"humanize-duration": "^3.32.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||
"jszip": "^3.10.1",
|
||||
"mkdir-recursive": "^0.4.0",
|
||||
"moment": "^2.30.1",
|
||||
"mqtt": "^5.3.4",
|
||||
"mqtt": "^5.5.4",
|
||||
"object-assign-deep": "^0.4.0",
|
||||
"rimraf": "^5.0.5",
|
||||
"semver": "^7.5.4",
|
||||
"semver": "^7.6.0",
|
||||
"source-map-support": "^0.5.21",
|
||||
"uri-js": "^4.4.1",
|
||||
"winston": "^3.11.0",
|
||||
"winston": "^3.13.0",
|
||||
"winston-syslog": "^2.7.0",
|
||||
"winston-transport": "^4.6.0",
|
||||
"ws": "^8.16.0",
|
||||
"zigbee-herdsman": "0.33.5",
|
||||
"zigbee-herdsman-converters": "18.25.1",
|
||||
"zigbee2mqtt-frontend": "0.6.152"
|
||||
"winston-transport": "^4.7.0",
|
||||
"ws": "^8.17.0",
|
||||
"zigbee-herdsman": "0.45.0",
|
||||
"zigbee-herdsman-converters": "19.32.0",
|
||||
"zigbee2mqtt-frontend": "0.6.165"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.23.7",
|
||||
"@babel/plugin-proposal-decorators": "^7.23.7",
|
||||
"@babel/preset-env": "^7.23.7",
|
||||
"@babel/preset-typescript": "^7.23.3",
|
||||
"@babel/core": "^7.24.4",
|
||||
"@babel/plugin-proposal-decorators": "^7.24.1",
|
||||
"@babel/preset-env": "^7.24.4",
|
||||
"@babel/preset-typescript": "^7.24.1",
|
||||
"@types/debounce": "^1.2.4",
|
||||
"@types/finalhandler": "^1.2.3",
|
||||
"@types/humanize-duration": "^3.27.3",
|
||||
"@types/jest": "^29.5.11",
|
||||
"@types/humanize-duration": "^3.27.4",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^20.10.6",
|
||||
"@types/node": "^20.12.7",
|
||||
"@types/object-assign-deep": "^0.4.3",
|
||||
"@types/readable-stream": "4.0.10",
|
||||
"@types/rimraf": "^3.0.2",
|
||||
"@types/readable-stream": "4.0.11",
|
||||
"@types/ws": "8.5.10",
|
||||
"@typescript-eslint/eslint-plugin": "^6.16.0",
|
||||
"@typescript-eslint/parser": "^6.16.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.7.1",
|
||||
"@typescript-eslint/parser": "^7.7.1",
|
||||
"babel-jest": "^29.7.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-plugin-jest": "^27.6.0",
|
||||
"eslint-plugin-jest": "^28.3.0",
|
||||
"jest": "^29.7.0",
|
||||
"tmp": "^0.2.1",
|
||||
"typescript": "^5.3.3"
|
||||
"tmp": "^0.2.3",
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"overrides": {
|
||||
"zigbee-herdsman-converters": {
|
||||
|
||||
@@ -100,7 +100,7 @@ for (const changelog of changelogs) {
|
||||
} else if (line === '# Changelog' || line === '### ⚠ BREAKING CHANGES' || !line) {
|
||||
continue;
|
||||
} else {
|
||||
throw new Error(`Unmatched line: ${line}`);
|
||||
changes['error'].push(`- Unmatched line: ${line}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import Availability from '../lib/extension/availability';
|
||||
import flushPromises from './lib/flushPromises';
|
||||
import stringify from 'json-stable-stringify-without-jsonify';
|
||||
|
||||
const mocks = [MQTT.publish, logger.warn, logger.debug];
|
||||
const mocks = [MQTT.publish, logger.warning, logger.debug];
|
||||
const devices = zigbeeHerdsman.devices;
|
||||
zigbeeHerdsman.returnDevices.push(
|
||||
...[devices.bulb_color.ieeeAddr, devices.bulb_color_2.ieeeAddr, devices.coordinator.ieeeAddr, devices.remote.ieeeAddr])
|
||||
|
||||
+31
-3
@@ -157,7 +157,7 @@ describe('Bind', () => {
|
||||
expect(target.configureReporting).toHaveBeenCalledWith("genOnOff",[{"attribute": "onOff", "maximumReportInterval": 0xFFFF, "minimumReportInterval": 0, "reportableChange": 0}]);
|
||||
expect(target.configureReporting).toHaveBeenCalledWith("genLevelCtrl",[{"attribute": "currentLevel", "maximumReportInterval": 0xFFFF, "minimumReportInterval": 5, "reportableChange": 1}]);
|
||||
expect(target.configureReporting).toHaveBeenCalledWith("lightingColorCtrl",[{"attribute":"colorTemperature","minimumReportInterval":5,"maximumReportInterval":0xFFFF,"reportableChange":1},{"attribute":"currentX","minimumReportInterval":5,"maximumReportInterval":0xFFFF,"reportableChange":1},{"attribute":"currentY","minimumReportInterval":5,"maximumReportInterval":0xFFFF,"reportableChange":1}]);
|
||||
expect(zigbeeHerdsman.devices.bulb_color.meta.configured).toBe(1324213189);
|
||||
expect(zigbeeHerdsman.devices.bulb_color.meta.configured).toBe(332242049);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/unbind',
|
||||
stringify({"data":{"from":"remote","to":"bulb_color","clusters":["genScenes","genOnOff","genLevelCtrl"],"failed":[]},"status":"ok"}),
|
||||
@@ -387,7 +387,7 @@ describe('Bind', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('Error bind fails when source not existing', async () => {
|
||||
it('Error bind fails when source device does not exist', async () => {
|
||||
const device = zigbeeHerdsman.devices.remote;
|
||||
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
|
||||
const endpoint = device.getEndpoint(1);
|
||||
@@ -401,7 +401,21 @@ describe('Bind', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('Error bind fails when target not existing', async () => {
|
||||
it("Error bind fails when source device's endpoint does not exist", async () => {
|
||||
const device = zigbeeHerdsman.devices.remote;
|
||||
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
|
||||
const endpoint = device.getEndpoint(1);
|
||||
mockClear(device);
|
||||
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote/not_existing_endpoint', to: 'bulb_color'}));
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({"data":{"from":"remote/not_existing_endpoint","to":"bulb_color"},"status":"error","error":"Source device 'remote' does not have endpoint 'not_existing_endpoint'"}),
|
||||
{retain: false, qos: 0}, expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Error bind fails when target device or group does not exist', async () => {
|
||||
const device = zigbeeHerdsman.devices.remote;
|
||||
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
|
||||
const endpoint = device.getEndpoint(1);
|
||||
@@ -415,6 +429,20 @@ describe('Bind', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("Error bind fails when target device's endpoint does not exist", async () => {
|
||||
const device = zigbeeHerdsman.devices.remote;
|
||||
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
|
||||
const endpoint = device.getEndpoint(1);
|
||||
mockClear(device);
|
||||
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote', to: 'bulb_color/not_existing_endpoint'}));
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({"data":{"from":"remote","to":"bulb_color/not_existing_endpoint"},"status":"error","error":"Target device 'bulb_color' does not have endpoint 'not_existing_endpoint'"}),
|
||||
{retain: false, qos: 0}, expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Legacy api: Should bind', async () => {
|
||||
const device = zigbeeHerdsman.devices.remote;
|
||||
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
|
||||
|
||||
+62
-11
File diff suppressed because one or more lines are too long
@@ -8,7 +8,7 @@ const flushPromises = require('./lib/flushPromises');
|
||||
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
const stringify = require('json-stable-stringify-without-jsonify');
|
||||
|
||||
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
|
||||
const mocksClear = [MQTT.publish, logger.warning, logger.debug];
|
||||
|
||||
describe('Configure', () => {
|
||||
let controller;
|
||||
@@ -24,7 +24,7 @@ describe('Configure', () => {
|
||||
const endpoint2 = device.getEndpoint(2);
|
||||
expect(endpoint2.write).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint2.write).toHaveBeenCalledWith("genBasic", {"49": {"type": 25, "value": 11}}, {"disableDefaultResponse": true, "manufacturerCode": 4107});
|
||||
expect(device.meta.configured).toBe(1);
|
||||
expect(device.meta.configured).toBe(332242049);
|
||||
}
|
||||
|
||||
const expectBulbConfigured = () => {
|
||||
@@ -176,11 +176,11 @@ describe('Configure', () => {
|
||||
});
|
||||
|
||||
it('Fail to configure via MQTT when device has no configure', async () => {
|
||||
await MQTT.events.message('zigbee2mqtt/bridge/request/device/configure', stringify({id: "0x0017880104e45521", transaction: 20}));
|
||||
await MQTT.events.message('zigbee2mqtt/bridge/request/device/configure', stringify({id: "0x0017882104a44559", transaction: 20}));
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/configure',
|
||||
stringify({"data":{"id": "0x0017880104e45521"},"status":"error","error": "Device 'button_double_key' cannot be configured","transaction":20}),
|
||||
stringify({"data":{"id": "0x0017882104a44559"},"status":"error","error": "Device 'TS0601_thermostat' cannot be configured","transaction":20}),
|
||||
{retain: false, qos: 0}, expect.any(Function)
|
||||
);
|
||||
});
|
||||
@@ -200,9 +200,9 @@ describe('Configure', () => {
|
||||
});
|
||||
|
||||
it('Legacy api: Should skip reconfigure when device does not require this', async () => {
|
||||
await MQTT.events.message('zigbee2mqtt/bridge/configure', '0x0017880104e45521');
|
||||
await MQTT.events.message('zigbee2mqtt/bridge/configure', '0x0017882104a44559');
|
||||
await flushPromises();
|
||||
expect(logger.warn).toHaveBeenCalledWith(`Skipping configure of 'button_double_key', device does not require this.`)
|
||||
expect(logger.warning).toHaveBeenCalledWith(`Skipping configure of 'TS0601_thermostat', device does not require this.`)
|
||||
});
|
||||
|
||||
it('Should not configure when interview not completed', async () => {
|
||||
|
||||
@@ -53,13 +53,13 @@ describe('Controller', () => {
|
||||
|
||||
it('Start controller', async () => {
|
||||
await controller.start();
|
||||
expect(zigbeeHerdsman.constructor).toHaveBeenCalledWith({"network":{"panID":6754,"extendedPanID":[221,221,221,221,221,221,221,221],"channelList":[11],"networkKey":[1,3,5,7,9,11,13,15,0,2,4,6,8,10,12,13]},"databasePath":path.join(data.mockDir, "database.db"), "databaseBackupPath":path.join(data.mockDir, "database.db.backup"),"backupPath":path.join(data.mockDir, "coordinator_backup.json"),"acceptJoiningDeviceHandler": expect.any(Function),adapter: {concurrent: null, delay: null, disableLED: false}, "serialPort":{"baudRate":undefined,"rtscts":undefined,"path":"/dev/dummy"}}, logger);
|
||||
expect(zigbeeHerdsman.constructor).toHaveBeenCalledWith({"network":{"panID":6754,"extendedPanID":[221,221,221,221,221,221,221,221],"channelList":[11],"networkKey":[1,3,5,7,9,11,13,15,0,2,4,6,8,10,12,13]},"databasePath":path.join(data.mockDir, "database.db"), "databaseBackupPath":path.join(data.mockDir, "database.db.backup"),"backupPath":path.join(data.mockDir, "coordinator_backup.json"),"acceptJoiningDeviceHandler": expect.any(Function),adapter: {concurrent: null, delay: null, disableLED: false}, "serialPort":{"baudRate":undefined,"rtscts":undefined,"path":"/dev/dummy"}});
|
||||
expect(zigbeeHerdsman.start).toHaveBeenCalledTimes(1);
|
||||
expect(zigbeeHerdsman.setTransmitPower).toHaveBeenCalledTimes(0);
|
||||
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledTimes(1);
|
||||
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledWith(true, undefined, undefined);
|
||||
expect(logger.info).toHaveBeenCalledWith(`Currently ${Object.values(zigbeeHerdsman.devices).length - 1} devices are joined:`)
|
||||
expect(logger.info).toHaveBeenCalledWith('bulb (0x000b57fffec6a5b2): LED1545G12 - IKEA TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white (Router)');
|
||||
expect(logger.info).toHaveBeenCalledWith('bulb (0x000b57fffec6a5b2): LED1545G12 - IKEA TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm (Router)');
|
||||
expect(logger.info).toHaveBeenCalledWith('remote (0x0017880104e45517): 324131092621 - Philips Hue dimmer switch (EndDevice)');
|
||||
expect(logger.info).toHaveBeenCalledWith('0x0017880104e45518 (0x0017880104e45518): Not supported (EndDevice)');
|
||||
expect(MQTT.connect).toHaveBeenCalledTimes(1);
|
||||
@@ -280,7 +280,7 @@ describe('Controller', () => {
|
||||
logger.debug.mockClear();
|
||||
await controller.mqtt.publish('skip-this-topic', '', {});
|
||||
await MQTT.events.message('zigbee2mqtt/skip-this-topic', 'skipped');
|
||||
expect(logger.debug).toHaveBeenCalledTimes(0);
|
||||
expect(logger.debug).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('On zigbee event message', async () => {
|
||||
@@ -398,7 +398,7 @@ describe('Controller', () => {
|
||||
const payload = {device, status: 'successful'};
|
||||
await zigbeeHerdsman.events.deviceInterview(payload);
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', stringify({"type":"pairing","message":"interview_successful","meta":{"friendly_name":"bulb","model":"LED1545G12","vendor":"IKEA","description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","supported":true}}), { retain: false, qos: 0 }, expect.any(Function));
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', stringify({"type":"pairing","message":"interview_successful","meta":{"friendly_name":"bulb","model":"LED1545G12","vendor":"IKEA","description":"TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm","supported":true}}), { retain: false, qos: 0 }, expect.any(Function));
|
||||
});
|
||||
|
||||
it('On zigbee deviceInterview successful not supported', async () => {
|
||||
|
||||
+24
-2
@@ -273,7 +273,7 @@ describe('Groups', () => {
|
||||
expect(logger.error).toHaveBeenCalledWith("Group 'group_1_not_existing' does not exist");
|
||||
});
|
||||
|
||||
it('Legacy api: Log when adding to non-existing device', async () => {
|
||||
it('Legacy api: Log when adding a non-existing device', async () => {
|
||||
await resetExtension();
|
||||
logger.error.mockClear();
|
||||
MQTT.events.message('zigbee2mqtt/bridge/group/group_1/add', 'bulb_color_not_existing');
|
||||
@@ -281,6 +281,14 @@ describe('Groups', () => {
|
||||
expect(logger.error).toHaveBeenCalledWith("Device 'bulb_color_not_existing' does not exist");
|
||||
});
|
||||
|
||||
it('Legacy api: Log when adding a non-existing endpoint', async () => {
|
||||
await resetExtension();
|
||||
logger.error.mockClear();
|
||||
MQTT.events.message('zigbee2mqtt/bridge/group/group_1/add', 'bulb_color/not_existing_endpoint');
|
||||
await flushPromises();
|
||||
expect(logger.error).toHaveBeenCalledWith("Device 'bulb_color' does not have endpoint 'not_existing_endpoint'");
|
||||
});
|
||||
|
||||
it('Should publish group state change when a device in it changes state', async () => {
|
||||
const device = zigbeeHerdsman.devices.bulb_color;
|
||||
const endpoint = device.getEndpoint(1);
|
||||
@@ -869,7 +877,7 @@ describe('Groups', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('Error when adding to non-existing device', async () => {
|
||||
it('Error when adding a non-existing device', async () => {
|
||||
await resetExtension();
|
||||
logger.error.mockClear();
|
||||
MQTT.publish.mockClear();
|
||||
@@ -883,6 +891,20 @@ describe('Groups', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('Error when adding a non-existing endpoint', async () => {
|
||||
await resetExtension();
|
||||
logger.error.mockClear();
|
||||
MQTT.publish.mockClear();
|
||||
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', device: 'bulb_color/not_existing_endpoint'}));
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/group/members/add',
|
||||
stringify({"data":{"device":"bulb_color/not_existing_endpoint","group":"group_1"},"status":"error","error":"Device 'bulb_color' does not have endpoint 'not_existing_endpoint'"}),
|
||||
{retain: false, qos: 0}, expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Should only include relevant properties when publishing member states', async () => {
|
||||
const bulbColor = zigbeeHerdsman.devices.bulb_color;
|
||||
const bulbColorTemp = zigbeeHerdsman.devices.bulb;
|
||||
|
||||
@@ -83,7 +83,6 @@ describe('HomeAssistant extension', () => {
|
||||
"availability":[{"topic":"zigbee2mqtt/bridge/state"}],
|
||||
"brightness":true,
|
||||
"brightness_scale":254,
|
||||
"color_mode":true,
|
||||
"command_topic":"zigbee2mqtt/ha_discovery_group/set",
|
||||
"device":{
|
||||
"identifiers":["zigbee2mqtt_1221051039810110150109113116116_9"],
|
||||
@@ -371,7 +370,6 @@ describe('HomeAssistant extension', () => {
|
||||
"availability":[{"topic":"zigbee2mqtt/bridge/state"}],
|
||||
"brightness":true,
|
||||
"brightness_scale":254,
|
||||
"color_mode": true,
|
||||
"supported_color_modes": ["color_temp"],
|
||||
"min_mireds": 250,
|
||||
"max_mireds": 454,
|
||||
@@ -381,7 +379,7 @@ describe('HomeAssistant extension', () => {
|
||||
"zigbee2mqtt_0x000b57fffec6a5b2"
|
||||
],
|
||||
"manufacturer":"IKEA",
|
||||
"model":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white (LED1545G12)",
|
||||
"model":"TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm (LED1545G12)",
|
||||
"name":"bulb",
|
||||
"sw_version": null,
|
||||
"via_device": "zigbee2mqtt_bridge_0x00124b00120144ae",
|
||||
@@ -993,9 +991,9 @@ describe('HomeAssistant extension', () => {
|
||||
|
||||
it('Should warn when starting with cache_state false', async () => {
|
||||
settings.set(['advanced', 'cache_state'], false);
|
||||
logger.warn.mockClear();
|
||||
logger.warning.mockClear();
|
||||
await resetExtension();
|
||||
expect(logger.warn).toHaveBeenCalledWith("In order for Home Assistant integration to work properly set `cache_state: true");
|
||||
expect(logger.warning).toHaveBeenCalledWith("In order for Home Assistant integration to work properly set `cache_state: true");
|
||||
});
|
||||
|
||||
it('Should set missing values to null', async () => {
|
||||
@@ -1369,7 +1367,6 @@ describe('HomeAssistant extension', () => {
|
||||
"availability":[{"topic":"zigbee2mqtt/bridge/state"}],
|
||||
"brightness":true,
|
||||
"brightness_scale":254,
|
||||
"color_mode":true,
|
||||
"command_topic":"zigbee2mqtt/ha_discovery_group_new/set",
|
||||
"device":{
|
||||
"identifiers":["zigbee2mqtt_1221051039810110150109113116116_9"],
|
||||
@@ -1482,7 +1479,7 @@ describe('HomeAssistant extension', () => {
|
||||
],
|
||||
"name":"bulb",
|
||||
'sw_version': null,
|
||||
"model":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white (LED1545G12)",
|
||||
"model":"TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm (LED1545G12)",
|
||||
"manufacturer":"IKEA",
|
||||
"via_device": "zigbee2mqtt_bridge_0x00124b00120144ae",
|
||||
},
|
||||
@@ -1916,7 +1913,6 @@ describe('HomeAssistant extension', () => {
|
||||
"availability":[{"topic":"zigbee2mqtt/bridge/state"}],
|
||||
"brightness":true,
|
||||
"brightness_scale":254,
|
||||
"color_mode":true,
|
||||
"command_topic":"zigbee2mqtt/ha_discovery_group/set",
|
||||
"device":{
|
||||
"identifiers":["zigbee2mqtt_1221051039810110150109113116116_9"],
|
||||
@@ -1970,7 +1966,6 @@ describe('HomeAssistant extension', () => {
|
||||
"availability":[{"topic":"zigbee2mqtt/bridge/state","value_template":'{{ value_json.state }}'}],
|
||||
"brightness":true,
|
||||
"brightness_scale":254,
|
||||
"color_mode":true,
|
||||
"command_topic":"zigbee2mqtt/ha_discovery_group/set",
|
||||
"device":{
|
||||
"identifiers":["zigbee2mqtt_1221051039810110150109113116116_9"],
|
||||
@@ -2030,14 +2025,13 @@ describe('HomeAssistant extension', () => {
|
||||
],
|
||||
"brightness":true,
|
||||
"brightness_scale":254,
|
||||
"color_mode":true,
|
||||
"command_topic":"zigbee2mqtt/bulb/set",
|
||||
"device":{
|
||||
"identifiers":[
|
||||
"zigbee2mqtt_0x000b57fffec6a5b2"
|
||||
],
|
||||
"manufacturer":"IKEA",
|
||||
"model":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white (LED1545G12)",
|
||||
"model":"TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm (LED1545G12)",
|
||||
"name":"bulb",
|
||||
"sw_version":null,
|
||||
"via_device": "zigbee2mqtt_bridge_0x00124b00120144ae",
|
||||
@@ -2088,7 +2082,7 @@ describe('HomeAssistant extension', () => {
|
||||
"zigbee2mqtt_0x000b57fffec6a5b2"
|
||||
],
|
||||
"manufacturer":"IKEA",
|
||||
"model":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white (LED1545G12)",
|
||||
"model":"TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm (LED1545G12)",
|
||||
"name":"bulb",
|
||||
"sw_version": null,
|
||||
"via_device": "zigbee2mqtt_bridge_0x00124b00120144ae",
|
||||
@@ -2530,7 +2524,8 @@ describe('HomeAssistant extension', () => {
|
||||
'origin': origin,
|
||||
'state_topic':'zigbee2mqtt/0x18fc26000000cafe',
|
||||
'unique_id':'0x18fc26000000cafe_device_mode_zigbee2mqtt',
|
||||
'value_template':'{{ value_json.device_mode }}'
|
||||
'value_template':'{{ value_json.device_mode }}',
|
||||
'enabled_by_default': true,
|
||||
}
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
"homeassistant/select/0x18fc26000000cafe/device_mode/config",
|
||||
|
||||
@@ -25,7 +25,7 @@ describe('Bridge legacy', () => {
|
||||
data.writeDefaultConfiguration();
|
||||
settings.reRead();
|
||||
logger.info.mockClear();
|
||||
logger.warn.mockClear();
|
||||
logger.warning.mockClear();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
@@ -183,7 +183,7 @@ describe('Bridge legacy', () => {
|
||||
const payload = JSON.parse(MQTT.publish.mock.calls[0][1]);
|
||||
expect(payload.length).toStrictEqual(Object.values(zigbeeHerdsman.devices).length);
|
||||
expect(payload[1]).toStrictEqual({"ieeeAddr": "0x00124b00120144ae", "type": "Coordinator", "dateCode": "20190425", "friendly_name": "Coordinator", networkAddress: 0, softwareBuildID: "z-Stack", lastSeen: 100});
|
||||
expect(payload[2]).toStrictEqual({"dateCode": null, "friendly_name": "bulb", "ieeeAddr": "0x000b57fffec6a5b2", "lastSeen": 1000, "manufacturerID": 4476, "model": "LED1545G12", "modelID": "TRADFRI bulb E27 WS opal 980lm", "networkAddress": 40369, "powerSource": "Mains (single phase)", "softwareBuildID": null, "type": "Router", "description": "TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white", "vendor": "IKEA"});
|
||||
expect(payload[2]).toStrictEqual({"dateCode": null, "friendly_name": "bulb", "ieeeAddr": "0x000b57fffec6a5b2", "lastSeen": 1000, "manufacturerID": 4476, "model": "LED1545G12", "modelID": "TRADFRI bulb E27 WS opal 980lm", "networkAddress": 40369, "powerSource": "Mains (single phase)", "softwareBuildID": null, "type": "Router", "description": "TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm", "vendor": "IKEA"});
|
||||
Date.now = now;
|
||||
});
|
||||
|
||||
@@ -478,6 +478,6 @@ describe('Bridge legacy', () => {
|
||||
MQTT.events.message('zigbee2mqtt/bridge/config/touchlink/factory_reset', '');
|
||||
await flushPromises();
|
||||
expect(zigbeeHerdsman.touchlinkFactoryResetFirst).toHaveBeenCalledTimes(1);
|
||||
expect(logger.warn).toHaveBeenCalledWith('Failed to factory reset device through Touchlink');
|
||||
expect(logger.warning).toHaveBeenCalledWith('Failed to factory reset device through Touchlink');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ const Controller = require('../../lib/controller');
|
||||
const flushPromises = require('../lib/flushPromises');
|
||||
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
|
||||
const mocksClear = [MQTT.publish, logger.warning, logger.debug];
|
||||
|
||||
describe('Report', () => {
|
||||
let controller;
|
||||
|
||||
+49
-55
@@ -25,16 +25,12 @@ describe('Logger', () => {
|
||||
|
||||
it('Create log directory', () => {
|
||||
const logger = require('../lib/util/logger').default;
|
||||
logger.init();
|
||||
logger.logOutput();
|
||||
const dirs = fs.readdirSync(dir.name);
|
||||
expect(dirs.length).toBe(1);
|
||||
});
|
||||
|
||||
it('Should cleanup', () => {
|
||||
const logger = require('../lib/util/logger').default;
|
||||
logger.init();
|
||||
logger.logOutput();
|
||||
|
||||
for (const d of fs.readdirSync(dir.name)) {
|
||||
rimrafSync(path.join(dir.name, d));
|
||||
@@ -51,53 +47,57 @@ describe('Logger', () => {
|
||||
|
||||
it('Should not cleanup when there is no timestamp set', () => {
|
||||
const logger = require('../lib/util/logger').default;
|
||||
logger.init();
|
||||
logger.logOutput();
|
||||
for (let i = 30; i < 40; i++) {
|
||||
fs.mkdirSync(path.join(dir.name, `log_${i}`));
|
||||
}
|
||||
|
||||
settings.set(['advanced', 'log_directory'], dir.name + '/bla');
|
||||
expect(fs.readdirSync(dir.name).length).toBe(20);
|
||||
expect(fs.readdirSync(dir.name).length).toBe(21);
|
||||
logger.cleanup();
|
||||
expect(fs.readdirSync(dir.name).length).toBe(20);
|
||||
expect(fs.readdirSync(dir.name).length).toBe(21);
|
||||
})
|
||||
|
||||
it('Set and get log level', () => {
|
||||
const logger = require('../lib/util/logger').default;
|
||||
logger.init();
|
||||
logger.logOutput();
|
||||
logger.setLevel('debug');
|
||||
expect(logger.getLevel()).toBe('debug');
|
||||
logger.setLevel('info');
|
||||
expect(logger.getLevel()).toBe('info');
|
||||
logger.setLevel('warning');
|
||||
expect(logger.getLevel()).toBe('warn');// match old Z2M levels
|
||||
logger.setLevel('warn');
|
||||
expect(logger.getLevel()).toBe('warn');// match old Z2M levels
|
||||
logger.setLevel('error');
|
||||
expect(logger.getLevel()).toBe('error');
|
||||
});
|
||||
|
||||
it('Add transport', () => {
|
||||
it('Set warning when log level is warn', () => {
|
||||
settings.set(['advanced', 'log_level'], 'warn');
|
||||
settings.reRead();
|
||||
const logger = require('../lib/util/logger').default;
|
||||
expect(logger.level).toBe('warning');// getLevel() reports old Z2M level to match display/value
|
||||
settings.set(['advanced', 'log_level'], 'info');
|
||||
settings.reRead();
|
||||
});
|
||||
|
||||
it('Add/remove transport', () => {
|
||||
class DummyTransport extends Transport {
|
||||
log(info, callback) {
|
||||
}
|
||||
}
|
||||
|
||||
const logger = require('../lib/util/logger').default;
|
||||
logger.init();
|
||||
expect(logger.winston().transports.length).toBe(2);
|
||||
logger.addTransport(new DummyTransport());
|
||||
expect(logger.winston().transports.length).toBe(3);
|
||||
});
|
||||
|
||||
it('Set and get log level warn <-> warning', () => {
|
||||
const logger = require('../lib/util/logger').default;
|
||||
logger.init();
|
||||
logger.logOutput();
|
||||
logger.setLevel('warn');
|
||||
expect(logger.winston().transports[0].level).toBe('warning');
|
||||
expect(logger.getLevel()).toBe('warn');
|
||||
expect(logger.winston.transports.length).toBe(2);
|
||||
const transport = new DummyTransport();
|
||||
logger.addTransport(transport);
|
||||
expect(logger.winston.transports.length).toBe(3);
|
||||
logger.removeTransport(transport);
|
||||
expect(logger.winston.transports.length).toBe(2);
|
||||
});
|
||||
|
||||
it('Logger should be console and file by default', () => {
|
||||
const logger = require('../lib/util/logger').default;
|
||||
logger.init();
|
||||
logger.logOutput();
|
||||
const pipes = logger.winston()._readableState.pipes;
|
||||
const pipes = logger.winston._readableState.pipes;
|
||||
expect(pipes.length).toBe(2);
|
||||
expect(pipes[0].constructor.name).toBe('Console');
|
||||
expect(pipes[0].silent).toBe(false);
|
||||
@@ -108,9 +108,7 @@ describe('Logger', () => {
|
||||
it('Logger can be file only', () => {
|
||||
settings.set(['advanced', 'log_output'], ['file']);
|
||||
const logger = require('../lib/util/logger').default;
|
||||
logger.init();
|
||||
logger.logOutput();
|
||||
const pipes = logger.winston()._readableState.pipes;
|
||||
const pipes = logger.winston._readableState.pipes;
|
||||
expect(pipes.length).toBe(2);
|
||||
expect(pipes[0].constructor.name).toBe('Console');
|
||||
expect(pipes[0].silent).toBe(true);
|
||||
@@ -121,9 +119,7 @@ describe('Logger', () => {
|
||||
it('Logger can be console only', () => {
|
||||
settings.set(['advanced', 'log_output'], ['console']);
|
||||
const logger = require('../lib/util/logger').default;
|
||||
logger.init();
|
||||
logger.logOutput();
|
||||
const pipes = logger.winston()._readableState.pipes;
|
||||
const pipes = logger.winston._readableState.pipes;
|
||||
expect(pipes.constructor.name).toBe('Console');
|
||||
expect(pipes.silent).toBe(false);
|
||||
});
|
||||
@@ -131,9 +127,7 @@ describe('Logger', () => {
|
||||
it('Logger can be nothing', () => {
|
||||
settings.set(['advanced', 'log_output'], []);
|
||||
const logger = require('../lib/util/logger').default;
|
||||
logger.init();
|
||||
logger.logOutput();
|
||||
const pipes = logger.winston()._readableState.pipes;
|
||||
const pipes = logger.winston._readableState.pipes;
|
||||
expect(pipes.constructor.name).toBe('Console');
|
||||
expect(pipes.silent).toBe(true);
|
||||
});
|
||||
@@ -141,9 +135,7 @@ describe('Logger', () => {
|
||||
it('Should allow to disable log rotation', () => {
|
||||
settings.set(['advanced', 'log_rotation'], false);
|
||||
const logger = require('../lib/util/logger').default;
|
||||
logger.init();
|
||||
logger.logOutput();
|
||||
const pipes = logger.winston()._readableState.pipes;
|
||||
const pipes = logger.winston._readableState.pipes;
|
||||
expect(pipes[1].constructor.name).toBe('File');
|
||||
expect(pipes[1].maxFiles).toBeNull();
|
||||
expect(pipes[1].tailable).toBeFalsy();
|
||||
@@ -153,8 +145,6 @@ describe('Logger', () => {
|
||||
it('Should allow to symlink logs to current directory', () => {
|
||||
settings.set(['advanced', 'log_symlink_current'], true);
|
||||
let logger = require('../lib/util/logger').default;
|
||||
logger.init();
|
||||
logger.logOutput();
|
||||
expect(fs.readdirSync(dir.name).includes('current')).toBeTruthy()
|
||||
|
||||
jest.resetModules();
|
||||
@@ -163,25 +153,29 @@ describe('Logger', () => {
|
||||
|
||||
it('Log', () => {
|
||||
const logger = require('../lib/util/logger').default;
|
||||
logger.init();
|
||||
const warn = jest.spyOn(logger.winston(), 'warning');
|
||||
logger.warn('warn');
|
||||
expect(warn).toHaveBeenCalledWith('warn');
|
||||
logger.setLevel('debug');
|
||||
|
||||
const debug = jest.spyOn(logger.winston(), 'debug');
|
||||
const debug = jest.spyOn(logger.winston, 'debug');
|
||||
logger.debug('debug');
|
||||
expect(debug).toHaveBeenCalledWith('debug');
|
||||
expect(debug).toHaveBeenCalledWith('debug', {namespace: 'z2m'});
|
||||
expect(debug).toHaveBeenCalledTimes(1);
|
||||
|
||||
const warning = jest.spyOn(logger.winston(), 'warning');
|
||||
logger.warning('warning');
|
||||
expect(warning).toHaveBeenCalledWith('warning');
|
||||
|
||||
const info = jest.spyOn(logger.winston(), 'info');
|
||||
const info = jest.spyOn(logger.winston, 'info');
|
||||
logger.info('info');
|
||||
expect(info).toHaveBeenCalledWith('info');
|
||||
expect(info).toHaveBeenCalledWith('info', {namespace: 'z2m'});
|
||||
expect(info).toHaveBeenCalledTimes(1);
|
||||
|
||||
const error = jest.spyOn(logger.winston(), 'error');
|
||||
const warning = jest.spyOn(logger.winston, 'warning');
|
||||
logger.warning('warning');
|
||||
expect(warning).toHaveBeenCalledWith('warning', {namespace: 'z2m'});
|
||||
expect(warning).toHaveBeenCalledTimes(1);
|
||||
|
||||
const error = jest.spyOn(logger.winston, 'error');
|
||||
logger.error('error');
|
||||
expect(error).toHaveBeenCalledWith('error');
|
||||
expect(error).toHaveBeenCalledWith('error', {namespace: 'z2m'});
|
||||
|
||||
logger.error(new Error('error'));// test for stack=true
|
||||
expect(error).toHaveBeenCalledWith('error', {namespace: 'z2m'});
|
||||
expect(error).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,10 +17,10 @@ const MQTT = require('./stub/mqtt');
|
||||
const settings = require('../lib/util/settings');
|
||||
const Controller = require('../lib/controller');
|
||||
const flushPromises = require('./lib/flushPromises');
|
||||
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
|
||||
const mocksClear = [MQTT.publish, logger.warning, logger.debug];
|
||||
const setTimeoutNative = setTimeout;
|
||||
|
||||
describe('onlythisNetworkmap', () => {
|
||||
describe('Networkmap', () => {
|
||||
let controller;
|
||||
|
||||
beforeAll(async () => {
|
||||
@@ -102,7 +102,7 @@ describe('onlythisNetworkmap', () => {
|
||||
let call = MQTT.publish.mock.calls[0];
|
||||
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/networkmap/raw');
|
||||
|
||||
const expected = {"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, color_options, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Wireless remote switch (double rocker), 2016 model","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Aqara"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":{"description":"Automatically generated definition","model":"notSupportedModelID","supports":"linkquality","vendor":"Boef"},"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]};
|
||||
const expected = {"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, color_options, identify, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Wireless remote switch (double rocker), 2016 model","model":"WXKG02LM_rev1","supports":"battery, voltage, power_outage_count, action, linkquality","vendor":"Aqara"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":{"description":"Automatically generated definition","model":"notSupportedModelID","supports":"action, linkquality","vendor":"Boef"},"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"CC2530 router","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]};
|
||||
expect(JSON.parse(call[1])).toStrictEqual(expected);
|
||||
|
||||
/**
|
||||
@@ -143,7 +143,7 @@ describe('onlythisNetworkmap', () => {
|
||||
const expected = `digraph G {
|
||||
node[shape=record];
|
||||
"0x00124b00120144ae" [style="bold, filled", fillcolor="#e04e5d", fontcolor="#ffffff", label="{Coordinator|0x00124b00120144ae (0x0000)|0 seconds ago}"];
|
||||
"0x000b57fffec6a5b2" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{bulb|0x000b57fffec6a5b2 (0x9db1)|IKEA TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white (LED1545G12)|9 seconds ago}"];
|
||||
"0x000b57fffec6a5b2" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{bulb|0x000b57fffec6a5b2 (0x9db1)|IKEA TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm (LED1545G12)|9 seconds ago}"];
|
||||
"0x000b57fffec6a5b2" -> "0x00124b00120144ae" [penwidth=2, weight=1, color="#009900", label="92 (routes: 0x198c)"]
|
||||
"0x000b57fffec6a5b3" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{bulb_color|0x000b57fffec6a5b3 (0x9dcf)|Philips Hue Go (7146060PH)|unknown}"];
|
||||
"0x000b57fffec6a5b3" -> "0x00124b00120144ae" [penwidth=0.5, weight=0, color="#994444", label="120"]
|
||||
@@ -151,7 +151,7 @@ describe('onlythisNetworkmap', () => {
|
||||
"0x0017880104e45521" [style="rounded, dashed, filled", fillcolor="#fff8ce", fontcolor="#000000", label="{button_double_key|0x0017880104e45521 (0x198a)|Aqara Wireless remote switch (double rocker), 2016 model (WXKG02LM_rev1)|9 seconds ago}"];
|
||||
"0x0017880104e45521" -> "0x0017880104e45559" [penwidth=1, weight=0, color="#994444", label="130"]
|
||||
"0x0017880104e45525" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{0x0017880104e45525|0x0017880104e45525 (0x1988)failed: lqi,routingTable|Boef Automatically generated definition (notSupportedModelID)|9 seconds ago}"];
|
||||
"0x0017880104e45559" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{cc2530_router|0x0017880104e45559 (0x198c)|Custom devices (DiY) [CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/) (CC2530.ROUTER)|9 seconds ago}"];
|
||||
"0x0017880104e45559" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{cc2530_router|0x0017880104e45559 (0x198c)|Custom devices (DiY) CC2530 router (CC2530.ROUTER)|9 seconds ago}"];
|
||||
"0x0017880104e45559" -> "0x000b57fffec6a5b2" [penwidth=0.5, weight=0, color="#994444", label="100"]
|
||||
"0x0017880104e45511" [style="rounded, dashed, filled", fillcolor="#fff8ce", fontcolor="#000000", label="{0x0017880104e45511|0x0017880104e45511 (0x045a)|external external (external_converter_device)|9 seconds ago}"];
|
||||
"0x0017880104e45511" -> "0x00124b00120144ae" [penwidth=1, weight=0, color="#994444", label="92"]
|
||||
@@ -207,7 +207,7 @@ describe('onlythisNetworkmap', () => {
|
||||
---
|
||||
0x000b57fffec6a5b2 (0x9db1)
|
||||
---
|
||||
IKEA TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white (LED1545G12)
|
||||
IKEA TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm (LED1545G12)
|
||||
---
|
||||
9 seconds ago
|
||||
]
|
||||
@@ -237,7 +237,7 @@ describe('onlythisNetworkmap', () => {
|
||||
---
|
||||
0x0017880104e45559 (0x198c)
|
||||
---
|
||||
Custom devices (DiY) [CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/) (CC2530.ROUTER)
|
||||
Custom devices (DiY) CC2530 router (CC2530.ROUTER)
|
||||
---
|
||||
9 seconds ago
|
||||
]
|
||||
@@ -276,7 +276,7 @@ describe('onlythisNetworkmap', () => {
|
||||
let call = MQTT.publish.mock.calls[0];
|
||||
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
|
||||
|
||||
const expected = {"data":{"routes":true,"type":"raw","value":{"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, color_options, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Wireless remote switch (double rocker), 2016 model","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Aqara"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":{"description":"Automatically generated definition","model":"notSupportedModelID","supports":"linkquality","vendor":"Boef"},"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]}},"status":"ok"};
|
||||
const expected = {"data":{"routes":true,"type":"raw","value":{"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, color_options, identify, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Wireless remote switch (double rocker), 2016 model","model":"WXKG02LM_rev1","supports":"battery, voltage, power_outage_count, action, linkquality","vendor":"Aqara"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":{"description":"Automatically generated definition","model":"notSupportedModelID","supports":"action, linkquality","vendor":"Boef"},"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"CC2530 router","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]}},"status":"ok"};
|
||||
const actual = JSON.parse(call[1]);
|
||||
expect(actual).toStrictEqual(expected);
|
||||
});
|
||||
@@ -303,7 +303,7 @@ describe('onlythisNetworkmap', () => {
|
||||
let call = MQTT.publish.mock.calls[0];
|
||||
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
|
||||
|
||||
const expected = {"data":{"routes":true,"type":"raw","value":{"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Wireless remote switch (double rocker), 2016 model","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Aqara"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":{"description":"Automatically generated definition","model":"notSupportedModelID","supports":"linkquality","vendor":"Boef"},"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]}},"status":"ok"}
|
||||
const expected = {"data":{"routes":true,"type":"raw","value":{"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Wireless remote switch (double rocker), 2016 model","model":"WXKG02LM_rev1","supports":"battery, voltage, power_outage_count, action, linkquality","vendor":"Aqara"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":{"description":"Automatically generated definition","model":"notSupportedModelID","supports":"action, linkquality","vendor":"Boef"},"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"CC2530 router","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]}},"status":"ok"}
|
||||
const actual = JSON.parse(call[1]);
|
||||
expect(actual).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ const settings = require('../lib/util/settings');
|
||||
const Controller = require('../lib/controller');
|
||||
const flushPromises = require('./lib/flushPromises');
|
||||
|
||||
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
|
||||
const mocksClear = [MQTT.publish, logger.warning, logger.debug];
|
||||
|
||||
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
|
||||
const mockOnEvent = jest.fn();
|
||||
|
||||
+16
-9
@@ -72,7 +72,7 @@ describe('OTA update', () => {
|
||||
mockClear(mapped);
|
||||
logger.info.mockClear();
|
||||
device.save.mockClear();
|
||||
mapped.ota.updateToLatest.mockImplementationOnce((a, b, onUpdate) => {
|
||||
mapped.ota.updateToLatest.mockImplementationOnce((a, onUpdate) => {
|
||||
onUpdate(0, null);
|
||||
onUpdate(10, 3600.2123);
|
||||
return 90;
|
||||
@@ -83,7 +83,7 @@ describe('OTA update', () => {
|
||||
expect(logger.info).toHaveBeenCalledWith(`Updating 'bulb' to latest firmware`);
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(0);
|
||||
expect(mapped.ota.updateToLatest).toHaveBeenCalledTimes(1);
|
||||
expect(mapped.ota.updateToLatest).toHaveBeenCalledWith(device, logger, expect.any(Function));
|
||||
expect(mapped.ota.updateToLatest).toHaveBeenCalledWith(device, expect.any(Function));
|
||||
expect(logger.info).toHaveBeenCalledWith(`Update of 'bulb' at 0.00%`);
|
||||
expect(logger.info).toHaveBeenCalledWith(`Update of 'bulb' at 10.00%, ≈ 60 minutes remaining`);
|
||||
expect(logger.info).toHaveBeenCalledWith(`Finished update of 'bulb'`);
|
||||
@@ -126,7 +126,7 @@ describe('OTA update', () => {
|
||||
const mapped = await zigbeeHerdsmanConverters.findByDevice(device)
|
||||
mockClear(mapped);
|
||||
device.save.mockClear();
|
||||
mapped.ota.updateToLatest.mockImplementationOnce((a, b, onUpdate) => {
|
||||
mapped.ota.updateToLatest.mockImplementationOnce((a, onUpdate) => {
|
||||
throw new Error('Update failed');
|
||||
});
|
||||
|
||||
@@ -260,7 +260,7 @@ describe('OTA update', () => {
|
||||
await zigbeeHerdsman.events.message(payload);
|
||||
await flushPromises();
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(1);
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledWith(device, logger, {"imageType": 12382});
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledWith(device, {"imageType": 12382});
|
||||
expect(logger.info).toHaveBeenCalledWith(`Update available for 'bulb'`);
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledTimes(1);
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 0x98}, undefined, 10);
|
||||
@@ -294,7 +294,7 @@ describe('OTA update', () => {
|
||||
await zigbeeHerdsman.events.message(payload);
|
||||
await flushPromises();
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(1);
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledWith(device, logger, {"imageType": 12382});
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledWith(device, {"imageType": 12382});
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledTimes(1);
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 0x98}, undefined, 10);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
@@ -316,7 +316,7 @@ describe('OTA update', () => {
|
||||
await zigbeeHerdsman.events.message(payload);
|
||||
await flushPromises();
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(1);
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledWith(device, logger, {"imageType": 12382});
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledWith(device, {"imageType": 12382});
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledTimes(1);
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 0x98}, undefined, 10);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
@@ -374,7 +374,7 @@ describe('OTA update', () => {
|
||||
logger.info.mockClear();
|
||||
logger.error.mockClear();
|
||||
device.save.mockClear();
|
||||
mapped.ota.updateToLatest.mockImplementationOnce((a, b, onUpdate) => {
|
||||
mapped.ota.updateToLatest.mockImplementationOnce((a, onUpdate) => {
|
||||
onUpdate(0, null);
|
||||
onUpdate(10, 3600);
|
||||
return 91;
|
||||
@@ -385,7 +385,7 @@ describe('OTA update', () => {
|
||||
expect(logger.info).toHaveBeenCalledWith(`Updating 'bulb' to latest firmware`);
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(0);
|
||||
expect(mapped.ota.updateToLatest).toHaveBeenCalledTimes(1);
|
||||
expect(mapped.ota.updateToLatest).toHaveBeenCalledWith(device, logger, expect.any(Function));
|
||||
expect(mapped.ota.updateToLatest).toHaveBeenCalledWith(device, expect.any(Function));
|
||||
expect(logger.info).toHaveBeenCalledWith(`Update of 'bulb' at 0.00%`);
|
||||
expect(logger.info).toHaveBeenCalledWith(`Update of 'bulb' at 10.00%, ≈ 60 minutes remaining`);
|
||||
expect(logger.info).toHaveBeenCalledWith(`Finished update of 'bulb'`);
|
||||
@@ -404,7 +404,7 @@ describe('OTA update', () => {
|
||||
logger.info.mockClear();
|
||||
logger.error.mockClear();
|
||||
device.save.mockClear();
|
||||
mapped.ota.updateToLatest.mockImplementationOnce((a, b, onUpdate) => {
|
||||
mapped.ota.updateToLatest.mockImplementationOnce((a, onUpdate) => {
|
||||
throw new Error('Update failed');
|
||||
});
|
||||
|
||||
@@ -485,4 +485,11 @@ describe('OTA update', () => {
|
||||
expect(spyUseIndexOverride).toHaveBeenCalledWith('http://my.site/index.json');
|
||||
spyUseIndexOverride.mockClear();
|
||||
});
|
||||
|
||||
it('Clear update state on startup', async () => {
|
||||
const device = controller.zigbee.resolveEntity(zigbeeHerdsman.devices.bulb_color.ieeeAddr);
|
||||
controller.state.set(device, {update: {progress: 100, remaining: 10, state: 'updating'}})
|
||||
await resetExtension();
|
||||
expect(controller.state.get(device)).toStrictEqual({update: {state: 'available'}});
|
||||
});
|
||||
});
|
||||
|
||||
+39
-5
@@ -9,7 +9,7 @@ const settings = require('../lib/util/settings');
|
||||
const Controller = require('../lib/controller');
|
||||
const flushPromises = require('./lib/flushPromises');
|
||||
|
||||
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
|
||||
const mocksClear = [MQTT.publish, logger.warning, logger.debug];
|
||||
|
||||
const expectNothingPublished = () => {
|
||||
Object.values(zigbeeHerdsman.devices).forEach((d) => {
|
||||
@@ -223,6 +223,40 @@ describe('Publish', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('Should publish messages to TuYa switch with dummy endpoints', async () => {
|
||||
const device = zigbeeHerdsman.devices.TS0601_switch;
|
||||
const endpoint = device.getEndpoint(1);
|
||||
await MQTT.events.message('zigbee2mqtt/TS0601_switch/set', stringify({state_l2: 'ON'}));
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith("manuSpecificTuya", "dataRequest", {dpValues: [{data: [1], datatype: 1, dp: 2}], seq: expect.any(Number)}, {disableDefaultResponse: true});
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/TS0601_switch',
|
||||
stringify({state_l2: 'ON'}),
|
||||
{ retain: false, qos: 0 },
|
||||
expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Should publish messages to TuYa cover switch with dummy endpoints', async () => {
|
||||
const device = zigbeeHerdsman.devices.TS0601_cover_switch;
|
||||
const endpoint = device.getEndpoint(1);
|
||||
await MQTT.events.message('zigbee2mqtt/TS0601_cover_switch/set', stringify({state: 'OPEN'}));
|
||||
await MQTT.events.message('zigbee2mqtt/TS0601_cover_switch/set', stringify({state_l1: 'ON'}));
|
||||
await MQTT.events.message('zigbee2mqtt/TS0601_cover_switch/l2/set', stringify({state: 'OFF'}));
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(3);
|
||||
expect(endpoint.command).toHaveBeenCalledWith("manuSpecificTuya", "dataRequest", {dpValues: [{data: [0], datatype: 4, dp: 1}], seq: expect.any(Number)}, {disableDefaultResponse: true});
|
||||
expect(endpoint.command).toHaveBeenCalledWith("manuSpecificTuya", "dataRequest", {dpValues: [{data: [1], datatype: 1, dp: 102}], seq: expect.any(Number)}, {disableDefaultResponse: true});
|
||||
expect(endpoint.command).toHaveBeenCalledWith("manuSpecificTuya", "dataRequest", {dpValues: [{data: [0], datatype: 1, dp: 101}], seq: expect.any(Number)}, {disableDefaultResponse: true});
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/TS0601_cover_switch',
|
||||
stringify({state_l2: 'OFF', state_l1: 'ON', state: 'OPEN'}),
|
||||
{ retain: false, qos: 0 },
|
||||
expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Should publish messages to zigbee devices with color xy', async () => {
|
||||
const device = zigbeeHerdsman.devices.bulb_color;
|
||||
const endpoint = device.getEndpoint(1);
|
||||
@@ -534,7 +568,7 @@ describe('Publish', () => {
|
||||
logger.error.mockClear();
|
||||
await MQTT.events.message('zigbee2mqtt/0x0017880104e45542/get', stringify({state_center: '', state_right: ''}));
|
||||
await flushPromises();
|
||||
expect(logger.error).toHaveBeenCalledWith(`Device 'wall_switch_double' has no endpoint 'center'`);
|
||||
expect(logger.error).toHaveBeenCalledWith(`No converter available for 'state_center' ("")`);
|
||||
expect(endpoint2.read).toHaveBeenCalledTimes(0);
|
||||
expect(endpoint3.read).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint3.read).toHaveBeenCalledWith('genOnOff', ['onOff']);
|
||||
@@ -975,7 +1009,7 @@ describe('Publish', () => {
|
||||
await MQTT.events.message('zigbee2mqtt/curtain/set', stringify(payload));
|
||||
await flushPromises();
|
||||
expect(endpoint.write).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {"85": {"type": 57, "value": 100}});
|
||||
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {presentValue: 100});
|
||||
});
|
||||
|
||||
it('ZNCLDJ11LM position', async () => {
|
||||
@@ -985,7 +1019,7 @@ describe('Publish', () => {
|
||||
await MQTT.events.message('zigbee2mqtt/curtain/set', stringify(payload));
|
||||
await flushPromises();
|
||||
expect(endpoint.write).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {"85": {"type": 57, "value": 10}});
|
||||
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {presentValue: 10});
|
||||
});
|
||||
|
||||
it('ZNCLDJ11LM position', async () => {
|
||||
@@ -995,7 +1029,7 @@ describe('Publish', () => {
|
||||
await MQTT.events.message('zigbee2mqtt/curtain/set', stringify(payload));
|
||||
await flushPromises();
|
||||
expect(endpoint.write).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {"85": {"type": 57, "value": 0}});
|
||||
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {presentValue: 0});
|
||||
});
|
||||
|
||||
it('ZNCLDJ11LM position', async () => {
|
||||
|
||||
@@ -8,7 +8,7 @@ const settings = require('../lib/util/settings');
|
||||
const Controller = require('../lib/controller');
|
||||
const flushPromises = require('./lib/flushPromises');
|
||||
|
||||
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
|
||||
const mocksClear = [MQTT.publish, logger.warning, logger.debug];
|
||||
|
||||
describe('Receive', () => {
|
||||
let controller;
|
||||
|
||||
@@ -882,18 +882,6 @@ describe('Settings', () => {
|
||||
expect(settings.validate()).toEqual(expect.arrayContaining([error]));
|
||||
});
|
||||
|
||||
it('Configuration shouldnt be valid when friendly_name is a postfix', async () => {
|
||||
write(configurationFile, {
|
||||
...minimalConfig,
|
||||
devices: {'0x0017880104e45519': {friendly_name: 'left', retain: false}},
|
||||
});
|
||||
|
||||
settings.reRead();
|
||||
|
||||
const error = `Following friendly_name are not allowed: '${utils.endpointNames}'`;
|
||||
expect(settings.validate()).toEqual(expect.arrayContaining([error]));
|
||||
});
|
||||
|
||||
it('Configuration shouldnt be valid when duplicate friendly_name are used', async () => {
|
||||
write(configurationFile, {
|
||||
devices: {
|
||||
|
||||
@@ -179,6 +179,12 @@ function writeDefaultConfiguration() {
|
||||
'0x0017882104a44559': {
|
||||
friendly_name: 'TS0601_thermostat',
|
||||
},
|
||||
'0x0017882104a44560': {
|
||||
friendly_name: 'TS0601_switch',
|
||||
},
|
||||
'0x0017882104a44562': {
|
||||
friendly_name: 'TS0601_cover_switch',
|
||||
},
|
||||
'0x0017882194e45543': {
|
||||
friendly_name: 'QS-Zigbee-D02-TRIAC-2C-LN',
|
||||
},
|
||||
|
||||
+7
-4
@@ -1,12 +1,12 @@
|
||||
let level = 'info';
|
||||
|
||||
const transports = [];
|
||||
let transports = [];
|
||||
|
||||
let transportsEnabled = false;
|
||||
const callTransports = (level, message) => {
|
||||
const callTransports = (level, message, namespace) => {
|
||||
if (transportsEnabled) {
|
||||
for (const transport of transports) {
|
||||
transport.log({level, message}, () => {});
|
||||
transport.log({level, message, namespace}, () => {});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,13 +14,16 @@ const callTransports = (level, message) => {
|
||||
const mock = {
|
||||
init: jest.fn(),
|
||||
info: jest.fn().mockImplementation((msg) => callTransports('info', msg)),
|
||||
warn: jest.fn().mockImplementation((msg) => callTransports('warn', msg)),
|
||||
warning: jest.fn().mockImplementation((msg) => callTransports('warning', msg)),
|
||||
error: jest.fn().mockImplementation((msg) => callTransports('error', msg)),
|
||||
debug: jest.fn().mockImplementation((msg) => callTransports('debug', msg)),
|
||||
cleanup: jest.fn(),
|
||||
logOutput: jest.fn(),
|
||||
add: (transport) => transports.push(transport),
|
||||
addTransport: (transport) => transports.push(transport),
|
||||
removeTransport: (transport) => {
|
||||
transports = transports.filter((t) => t !== transport);
|
||||
},
|
||||
setLevel: (newLevel) => {level = newLevel},
|
||||
getLevel: () => level,
|
||||
setTransportsEnabled: (value) => {transportsEnabled = value},
|
||||
|
||||
@@ -134,6 +134,8 @@ const bulb_color = new Device('Router', '0x000b57fffec6a5b3', 40399, 4107, [new
|
||||
const bulb_color_2 = new Device('Router', '0x000b57fffec6a5b4', 401292, 4107, [new Endpoint(1, [0,3,4,5,6,8,768,2821,4096], [5,25,32,4096], '0x000b57fffec6a5b4', [], {lightingColorCtrl: {colorCapabilities: 254}}, [], null, null, {'scenes': {'1_0': {name: 'Chill scene', state: {state: 'ON'}}, '4_9': {state: {state: 'OFF'}}}})], true, "Mains (single phase)", "LLC020", false, 'Philips', '2019.09', '5.127.1.26581');
|
||||
const bulb_2 = new Device('Router', '0x000b57fffec6a5b7', 40369, 4476, [new Endpoint(1, [0,3,4,5,6,8,768,2821,4096], [5,25,32,4096], '0x000b57fffec6a5b7', [], {lightingColorCtrl: {colorCapabilities: 17}})], true, "Mains (single phase)", "TRADFRI bulb E27 WS opal 980lm");
|
||||
const TS0601_thermostat = new Device('EndDevice', '0x0017882104a44559', 6544,4151, [new Endpoint(1, [], [], '0x0017882104a44559')], true, "Mains (single phase)", 'kud7u2l');
|
||||
const TS0601_switch = new Device('EndDevice', '0x0017882104a44560', 6544,4151, [new Endpoint(1, [], [], '0x0017882104a44560')], true, "Mains (single phase)", 'kjintbl');
|
||||
const TS0601_cover_switch = new Device('EndDevice', '0x0017882104a44562', 6544,4151, [new Endpoint(1, [], [], '0x0017882104a44562')], true, "Mains (single phase)", 'TS0601', false, '_TZE200_5nldle7w');
|
||||
const ZNCZ02LM = new Device('Router', '0x0017880104e45524', 6540,4151, [new Endpoint(1, [0, 6], [], '0x0017880104e45524')], true, "Mains (single phase)", "lumi.plug");
|
||||
const GLEDOPTO_2ID = new Device('Router', '0x0017880104e45724', 6540,4151, [new Endpoint(11, [0,3,4,5,6,8,768], [], '0x0017880104e45724', [], {}, [], 49246, 528), new Endpoint(12, [0, 3, 4, 5, 6, 8, 768], [], '0x0017880104e45724', [], {}, [], 260, 258), new Endpoint(13, [4096], [4096], '0x0017880104e45724', [], {}, [], 49246, 57694), new Endpoint(15, [0, 3, 4, 5, 6, 8, 768], [], '0x0017880104e45724', [], {}, [], 49246, 256)], true, "Mains (single phase)", 'GL-C-007', false, 'GLEDOPTO');
|
||||
const QBKG03LM = new Device('Router', '0x0017880104e45542', 6540,4151, [new Endpoint(1, [0], [], '0x0017880104e45542'), new Endpoint(2, [0, 6], [], '0x0017880104e45542'), new Endpoint(3, [0, 6], [], '0x0017880104e45542')], true, "Mains (single phase)", 'lumi.ctrl_neutral2');
|
||||
@@ -197,6 +199,8 @@ const devices = {
|
||||
'3157100': new Device('Router', '0x0017880104e44559', 6542,4151, [new Endpoint(1, [], [], '0x0017880104e44559')], true, "Mains (single phase)", '3157100', false, 'Centralite'),
|
||||
'J1': new Device('Router', '0x0017880104a44559', 6543,4151, [new Endpoint(1, [], [], '0x0017880104a44559')], true, "Mains (single phase)", 'J1 (5502)'),
|
||||
'TS0601_thermostat': TS0601_thermostat,
|
||||
'TS0601_switch': TS0601_switch,
|
||||
'TS0601_cover_switch': TS0601_cover_switch,
|
||||
'external_converter_device': new Device('EndDevice', '0x0017880104e45511', 1114, 'external', [new Endpoint(1, [], [], '0x0017880104e45511')], false, null, 'external_converter_device' ),
|
||||
'QS_Zigbee_D02_TRIAC_2C_LN':new Device('Router', '0x0017882194e45543', 6549,4151, [new Endpoint(1, [0], [], '0x0017882194e45543'), new Endpoint(2, [0, 6], [], '0x0017882194e45543')], true, "Mains (single phase)", 'TS110F', false, '_TYZB01_v8gtiaed'),
|
||||
'unknown': new Device('Router', '0x0017980134e45545', 6540,4151, [], true, "Mains (single phase)"),
|
||||
@@ -257,8 +261,8 @@ const mock = {
|
||||
const mockConstructor = jest.fn().mockImplementation(() => mock);
|
||||
|
||||
jest.mock('zigbee-herdsman', () => ({
|
||||
...jest.requireActual('zigbee-herdsman'),
|
||||
Controller: mockConstructor,
|
||||
Zcl: {ManufacturerCode: {Philips: 4107}},
|
||||
}));
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
#!/bin/bash -e
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
NEED_RESTART=0
|
||||
|
||||
if [ -d data-backup ]; then
|
||||
echo "ERROR: Backup directory exists. May be previous restoring was failed?"
|
||||
echo "1. Save 'data-backup' and 'data' dirs to safe location to make possibility to restore config later."
|
||||
echo "2. Manually delete 'data-backup' dir and try again."
|
||||
exit 1
|
||||
echo "ERROR: Backup directory exists. May be previous restoring was failed?"
|
||||
echo "1. Save 'data-backup' and 'data' dirs to safe location to make possibility to restore config later."
|
||||
echo "2. Manually delete 'data-backup' dir and try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if which systemctl 2> /dev/null > /dev/null; then
|
||||
echo "Stopping Zigbee2MQTT..."
|
||||
sudo systemctl stop zigbee2mqtt
|
||||
echo "Checking Zigbee2MQTT status..."
|
||||
if systemctl is-active --quiet zigbee2mqtt; then
|
||||
echo "Stopping Zigbee2MQTT..."
|
||||
sudo systemctl stop zigbee2mqtt
|
||||
NEED_RESTART=1
|
||||
fi
|
||||
else
|
||||
echo "Skipped stopping Zigbee2MQTT, no systemctl found"
|
||||
echo "Skipped stopping Zigbee2MQTT, no systemctl found"
|
||||
fi
|
||||
|
||||
echo "Creating backup of configuration..."
|
||||
@@ -34,11 +40,9 @@ echo "Restore configuration..."
|
||||
cp -R data-backup/* data
|
||||
rm -rf data-backup
|
||||
|
||||
if which systemctl 2> /dev/null > /dev/null; then
|
||||
echo "Starting Zigbee2MQTT..."
|
||||
sudo systemctl start zigbee2mqtt
|
||||
else
|
||||
echo "Skipped starting Zigbee2MQTT, no systemctl found"
|
||||
if [ $NEED_RESTART -eq 1 ]; then
|
||||
echo "Starting Zigbee2MQTT..."
|
||||
sudo systemctl start zigbee2mqtt
|
||||
fi
|
||||
|
||||
echo "Done!"
|
||||
|
||||
Reference in New Issue
Block a user