Compare commits

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

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

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

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

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

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

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

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

* Fix tests

---------

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

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

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

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

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

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

* Fix tests

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

* Fix tests

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

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

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

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

* Fix tests

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

* Fix linting error

* Add setting "cache_state_persist_on_leave" (default false)

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

* Merge from upstream kk/dev

* Re-define cache_state_persist_on_leave to be a timeout

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

Remove all pending deletions if the system is stopped

* Add debug. Fix "time" test

* Disable esline warnings for long debug lines

* Simplify state maintenance for leave/join devices

* Restore default configuration.yaml

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

* Update configuration.yaml

* Update state.ts

* Update state.ts

* Update controller.test.js

* Update eventBus.ts

* Update eventBus.ts

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

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

* Updates

* Tests

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

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

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

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

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

* fix tests

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

* Update Dockerfile

* Update Dockerfile

* Update Dockerfile

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

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

* Taking all PR remarks in account.

Co-authored-by: phil <phil@8godsprod.com>
2022-12-04 19:33:04 +01:00
github-actions[bot]andKoenkk 959ca95219 Update dependencies (#15342)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-04 19:13:38 +01:00
github-actions[bot]andKoenkk 54bd9aa024 Update zigbee-herdsman-converters to 14.0.682 (#15320)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-02 16:55:00 +00:00
github-actions[bot]andKoenkk 0e4ea6a335 Update zigbee-herdsman-converters to 14.0.681 (#15287)
Co-authored-by: Koenkk <Koenkk@users.noreply.github.com>
2022-12-01 19:18:59 +00:00
Koen Kanters ab1261385b Promote to dev 2022-12-01 19:59:17 +01:00
Koen Kanters 52e545f900 1.28.4 2022-12-01 19:58:42 +01:00
Koen Kanters 350abb354b Undev 2022-12-01 19:58:39 +01:00
Koen Kanters 87f253997c Update zhc to 14.0.681-hotfix.0 2022-12-01 19:56:37 +01:00
Koen Kanters 4e9b18fcbf Promote to dev 2022-12-01 17:38:11 +01:00
40 changed files with 2197 additions and 2213 deletions
+5 -3
View File
@@ -12,7 +12,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
registry-url: https://registry.npmjs.org/
cache: 'npm'
- name: Install dependencies
@@ -45,8 +45,9 @@ jobs:
run: |
docker buildx build \
--build-arg COMMIT=$(git rev-parse --short HEAD) \
--platform linux/arm64/v8,linux/amd64,linux/arm/v6,linux/arm/v7 \
--platform linux/arm64/v8,linux/386,linux/amd64,linux/arm/v6,linux/arm/v7 \
-f docker/Dockerfile \
--provenance=false \
--push \
-t koenkk/zigbee2mqtt:latest-dev -t ghcr.io/koenkk/zigbee2mqtt:latest-dev \
.
@@ -56,8 +57,9 @@ jobs:
TAG="$(git describe --tags)"
docker buildx build \
--build-arg COMMIT=$(git rev-parse --short HEAD) \
--platform linux/arm64/v8,linux/amd64,linux/arm/v6,linux/arm/v7 \
--platform linux/arm64/v8,linux/386,linux/amd64,linux/arm/v6,linux/arm/v7 \
-f docker/Dockerfile \
--provenance=false \
--push \
-t koenkk/zigbee2mqtt:latest -t "koenkk/zigbee2mqtt:$TAG" -t ghcr.io/koenkk/zigbee2mqtt:latest -t "ghcr.io/koenkk/zigbee2mqtt:$TAG" \
.
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
pull-requests: write # for actions/stale to close stale PRs
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v6
- uses: actions/stale@v7
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'
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- run: npx npm-check-updates -u -x connect-gzip-static # connect-gzip-static only supports node 16
- run: npm install
- uses: peter-evans/create-pull-request@v4
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- run: npm install zigbee2mqtt-frontend@${{ github.event.client_payload.version }} --save-exact
- uses: peter-evans/create-pull-request@v4
id: cpr
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- run: npm install zigbee-herdsman@${{ github.event.client_payload.version }} --save-exact
- uses: peter-evans/create-pull-request@v4
id: cpr
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- run: npm install zigbee-herdsman-converters@${{ github.event.client_payload.version }} --save-exact
- uses: peter-evans/create-pull-request@v4
id: cpr
+6 -4
View File
@@ -1,7 +1,7 @@
FROM node:18-alpine3.16 as base
FROM alpine:3.17.2 as base
WORKDIR /app
RUN apk add --no-cache tzdata eudev tini
RUN apk add --no-cache tzdata eudev tini nodejs
# Dependencies and build
FROM base as dependencies_and_build
@@ -9,8 +9,10 @@ FROM base as dependencies_and_build
COPY package*.json tsconfig.json index.js ./
COPY lib ./lib
RUN apk add --no-cache --virtual .buildtools make gcc g++ python3 linux-headers git && \
RUN apk add --no-cache --virtual .buildtools make gcc g++ python3 linux-headers git npm && \
npm ci --production --no-audit --no-optional --no-update-notifier && \
# Serialport needs to be rebuild for Alpine https://serialport.io/docs/9.x.x/guide-installation#alpine-linux
npm rebuild --build-from-source && \
apk del .buildtools
# Release
@@ -31,4 +33,4 @@ RUN echo "$COMMIT" > dist/.hash
ENV NODE_ENV production
ENTRYPOINT ["docker-entrypoint.sh"]
CMD [ "/sbin/tini", "--", "node", "index.js"]
CMD [ "/sbin/tini", "--", "node", "index.js"]
+5 -2
View File
@@ -57,7 +57,7 @@ export class Controller {
});
this.zigbee = new Zigbee(this.eventBus);
this.mqtt = new MQTT(this.eventBus);
this.state = new State(this.eventBus);
this.state = new State(this.eventBus, this.zigbee);
this.restartCallback = restartCallback;
this.exitCallback = exitCallback;
@@ -243,11 +243,14 @@ export class Controller {
message.device = {
friendlyName: entity.name, model: entity.definition ? entity.definition.model : 'unknown',
ieeeAddr: entity.ieeeAddr, networkAddress: entity.zh.networkAddress, type: entity.zh.type,
manufacturerID: entity.zh.manufacturerID, manufacturerName: entity.zh.manufacturerName,
manufacturerID: entity.zh.manufacturerID,
powerSource: entity.zh.powerSource, applicationVersion: entity.zh.applicationVersion,
stackVersion: entity.zh.stackVersion, zclVersion: entity.zh.zclVersion,
hardwareVersion: entity.zh.hardwareVersion, dateCode: entity.zh.dateCode,
softwareBuildID: entity.zh.softwareBuildID,
// Manufacturer name can contain \u0000, remove this.
// https://github.com/home-assistant/core/issues/85691
manufacturerName: entity.zh.manufacturerName?.split('\u0000')[0],
};
}
+3
View File
@@ -102,6 +102,7 @@ const pollOnMessage: PollOnMessage = [
zigbeeHersdman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
zigbeeHersdman.Zcl.ManufacturerCode.MUELLER_LICHT_INT,
zigbeeHersdman.Zcl.ManufacturerCode.TELINK,
zigbeeHersdman.Zcl.ManufacturerCode.BUSCH_JAEGER,
],
manufacturerNames: [
'GLEDOPTO',
@@ -137,6 +138,7 @@ const pollOnMessage: PollOnMessage = [
zigbeeHersdman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
zigbeeHersdman.Zcl.ManufacturerCode.MUELLER_LICHT_INT,
zigbeeHersdman.Zcl.ManufacturerCode.TELINK,
zigbeeHersdman.Zcl.ManufacturerCode.BUSCH_JAEGER,
],
manufacturerNames: [
'GLEDOPTO',
@@ -168,6 +170,7 @@ const pollOnMessage: PollOnMessage = [
zigbeeHersdman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
zigbeeHersdman.Zcl.ManufacturerCode.MUELLER_LICHT_INT,
zigbeeHersdman.Zcl.ManufacturerCode.TELINK,
// Note: ManufacturerCode.BUSCH_JAEGER is left out intentionally here as their devices don't support colors
],
manufacturerNames: [
'GLEDOPTO',
+12 -34
View File
@@ -6,7 +6,6 @@ import Transport from 'winston-transport';
import bind from 'bind-decorator';
import stringify from 'json-stable-stringify-without-jsonify';
import objectAssignDeep from 'object-assign-deep';
import {detailedDiff} from 'deep-object-diff';
import Extension from './extension';
import Device from '../model/device';
import Group from '../model/group';
@@ -148,37 +147,7 @@ export default class Bridge extends Extension {
throw new Error(`Invalid payload`);
}
const diff: KeyValue = detailedDiff(settings.get(), message.options);
// Remove any settings that are in the deleted.diff but not in the passed options
const cleanupDeleted = (options: KeyValue, deleted: KeyValue): void => {
for (const key of Object.keys(deleted)) {
if (!(key in options)) {
delete deleted[key];
} else if (!Array.isArray(options[key])) {
cleanupDeleted(options[key], deleted[key]);
}
}
};
cleanupDeleted(message.options, diff.deleted);
// objectAssignDeep requires object prototype which is missing from detailedDiff, therefore clone
const newSettings = objectAssignDeep({}, utils.clone(diff.added), utils.clone(diff.updated),
utils.clone(diff.deleted));
// deep-object-diff converts arrays to objects, set original array back here
const convertBackArray = (before: KeyValue, after: KeyValue): void => {
for (const [key, afterValue] of Object.entries(after)) {
const beforeValue = before[key];
if (Array.isArray(beforeValue)) {
after[key] = beforeValue;
} else if (afterValue && typeof beforeValue === 'object') {
convertBackArray(beforeValue, afterValue);
}
}
};
convertBackArray(message.options, newSettings);
const newSettings = utils.computeSettingsToChange(settings.get(), message.options);
const restartRequired = settings.apply(newSettings);
if (restartRequired) this.restartRequired = true;
@@ -421,15 +390,23 @@ export default class Bridge extends Extension {
const ID = message.id;
const entity = this.getEntity(entityType, ID);
const currentOptions = entityType === 'device' ? settings.get().devices[entity.ID] :
settings.get().groups[entity.ID];
const options = utils.computeSettingsToChange(currentOptions, message.options);
const oldOptions = objectAssignDeep({}, cleanup(entity.options));
settings.changeEntityOptions(ID, message.options);
const restartRequired = settings.changeEntityOptions(ID, options);
if (restartRequired) this.restartRequired = true;
const newOptions = cleanup(entity.options);
await this.publishInfo();
logger.info(`Changed config for ${entityType} ${ID}`);
this.eventBus.emitEntityOptionsChanged({from: oldOptions, to: newOptions, entity});
return utils.getResponse(message, {from: oldOptions, to: newOptions, id: ID}, null);
return utils.getResponse(
message,
{from: oldOptions, to: newOptions, id: ID, restart_required: this.restartRequired},
null,
);
}
@bind async deviceConfigureReporting(message: string | KeyValue): Promise<MQTTResponse> {
@@ -679,6 +656,7 @@ export default class Bridge extends Extension {
network_address: device.zh.networkAddress,
supported: !!device.definition,
friendly_name: device.name,
disabled: !!device.options.disabled,
description: device.options.description,
definition: this.getDefinitionPayload(device),
power_source: device.zh.powerSource,
+6 -4
View File
@@ -64,9 +64,11 @@ export default class Configure extends Extension {
}
override async start(): Promise<void> {
for (const device of this.zigbee.devices(false)) {
await this.configure(device, 'started');
}
setImmediate(async () => {
for (const device of this.zigbee.devices(false)) {
await this.configure(device, 'started');
}
});
this.eventBus.onDeviceJoined(this, (data) => {
if (data.device.zh.meta.hasOwnProperty('configured')) {
@@ -85,7 +87,7 @@ export default class Configure extends Extension {
private async configure(device: Device, event: 'started' | 'zigbee_event' | 'reporting_disabled' | 'mqtt_message',
force=false, thowError=false): Promise<void> {
if (!force) {
if (!device.definition?.configure || !device.zh.interviewCompleted) {
if (device.options.disabled || !device.definition?.configure || !device.zh.interviewCompleted) {
return;
}
+2 -2
View File
@@ -56,7 +56,7 @@ export default class ExternalExtension extends Extension {
@bind private async saveExtension(message: KeyValue): Promise<MQTTResponse> {
const {name, code} = message;
const ModuleConstructor = utils.loadModuleFromText(code) as typeof Extension;
const ModuleConstructor = utils.loadModuleFromText(code, name) as typeof Extension;
await this.loadExtension(ModuleConstructor);
const basePath = this.getExtensionsBasePath();
/* istanbul ignore else */
@@ -95,7 +95,7 @@ export default class ExternalExtension extends Extension {
private loadUserDefinedExtensions(): void {
const extensions = this.getListOfUserDefinedExtensions();
extensions
.map(({code}) => utils.loadModuleFromText(code))
.map(({code, name}) => utils.loadModuleFromText(code, name))
.map(this.loadExtension);
}
+31 -4
View File
@@ -1,4 +1,5 @@
import http from 'http';
import https from 'https';
import gzipStatic, {RequestHandler} from 'connect-gzip-static';
import finalhandler from 'finalhandler';
import logger from '../util/logger';
@@ -6,6 +7,7 @@ import frontend from 'zigbee2mqtt-frontend';
import WebSocket from 'ws';
import net from 'net';
import url from 'url';
import fs from 'fs';
import * as settings from '../util/settings';
import utils from '../util/utils';
import stringify from 'json-stable-stringify-without-jsonify';
@@ -19,6 +21,8 @@ export default class Frontend extends Extension {
private mqttBaseTopic = settings.get().mqtt.base_topic;
private host = settings.get().frontend.host;
private port = settings.get().frontend.port;
private sslCert = settings.get().frontend.ssl_cert;
private sslKey = settings.get().frontend.ssl_key;
private authToken = settings.get().frontend.auth_token;
private retainedMessages = new Map();
private server: http.Server;
@@ -33,8 +37,28 @@ export default class Frontend extends Extension {
this.eventBus.onMQTTMessagePublished(this, this.onMQTTPublishMessage);
}
private isHttpsConfigured():boolean {
if (this.sslCert && this.sslKey) {
if (!fs.existsSync(this.sslCert) || !fs.existsSync(this.sslKey)) {
logger.error(`defined ssl_cert '${this.sslCert}' or ssl_key '${this.sslKey}' file path does not exists, server won't be secured.`); /* eslint-disable-line max-len */
return false;
}
return true;
}
return false;
}
override async start(): Promise<void> {
this.server = http.createServer(this.onRequest);
if (this.isHttpsConfigured()) {
const serverOptions = {
key: fs.readFileSync(this.sslKey),
cert: fs.readFileSync(this.sslCert)};
this.server = https.createServer(serverOptions, this.onRequest);
} else {
this.server = http.createServer(this.onRequest);
}
this.server.on('upgrade', this.onUpgrade);
/* istanbul ignore next */
@@ -56,12 +80,15 @@ export default class Frontend extends Extension {
override async stop(): Promise<void> {
super.stop();
for (const client of this.wss.clients) {
this.wss?.clients.forEach((client) => {
client.send(stringify({topic: 'bridge/state', payload: 'offline'}));
client.terminate();
});
this.wss?.close();
/* istanbul ignore else */
if (this.server) {
return new Promise((cb: () => void) => this.server.close(cb));
}
this.wss.close();
return new Promise((cb: () => void) => this.server.close(cb));
}
@bind private onRequest(request: http.IncomingMessage, response: http.ServerResponse): void {
+1
View File
@@ -142,6 +142,7 @@ export default class Groups extends Extension {
const groupsToPublish: Set<Group> = new Set();
for (const member of entity.zh.members) {
const device = this.zigbee.resolveEntity(member.getDevice()) as Device;
if (device.options.disabled) continue;
const exposes = device.exposes();
const memberPayload: KeyValue = {};
Object.keys(payload).forEach((key) => {
+38 -19
View File
@@ -390,8 +390,8 @@ export default class HomeAssistant extends Extension {
?.features.find((f) => f.name === 'position');
const tilt = exposes.find((expose) => expose.features.find((e) => e.name === 'tilt'))
?.features.find((f) => f.name === 'tilt');
const motorState = allExposes?.find((e) => e.type === 'enum' && e.name === 'motor_state' &&
e.access === ACCESS_STATE);
const motorState = allExposes?.find((e) => e.type === 'enum' &&
['motor_state', 'moving'].includes(e.name) && e.access === ACCESS_STATE);
const running = allExposes?.find((e) => e.type === 'binary' && e.name === 'running');
const discoveryEntry: DiscoveryEntry = {
@@ -406,9 +406,16 @@ export default class HomeAssistant extends Extension {
},
};
// For curtains that have `motor_state` lookup a possible state names and make this
// available for discovery. If the curtains only support the `running` value,
// then we use it anyway. The movement direction is calculated (assumed) in this case.
// If curtains have `running` property, use this in discovery.
// The movement direction is calculated (assumed) in this case.
if (running) {
discoveryEntry.discovery_payload.value_template = `{% if "${running.property}" in value_json ` +
`and value_json.${running.property} %} {% if value_json.${position.property} > 0 %} closing ` +
`{% else %} opening {% endif %} {% else %} stopped {% endif %}`;
}
// If curtains have `motor_state` or `moving` property, lookup for possible
// state names to detect movement direction and use this in discovery.
if (motorState) {
const openingLookup = ['opening', 'open', 'forward', 'up', 'rising'];
const closingLookup = ['closing', 'close', 'backward', 'back', 'reverse', 'down', 'declining'];
@@ -422,14 +429,14 @@ export default class HomeAssistant extends Extension {
discoveryEntry.discovery_payload.state_opening = openingState;
discoveryEntry.discovery_payload.state_closing = closingState;
discoveryEntry.discovery_payload.state_stopped = stoppedState;
discoveryEntry.discovery_payload.value_template = `{% if not value_json.${motorState.property} %}` +
` ${stoppedState} {% else %} {{ value_json.${motorState.property} }} {% endif %}`;
discoveryEntry.discovery_payload.value_template = `{% if "${motorState.property}" in value_json ` +
`and value_json.${motorState.property} %} {{ value_json.${motorState.property} }} {% else %} ` +
`${stoppedState} {% endif %}`;
}
} else if (running) {
discoveryEntry.discovery_payload.value_template = `{% if not value_json.${running.property} %} ` +
`stopped {% else %} {% if value_json.${position.property} > 0 %} closing {% else %} ` +
`opening {% endif %} {% endif %}`;
} else {
}
// If curtains do not have `running`, `motor_state` or `moving` properties.
if (!discoveryEntry.discovery_payload.value_template) {
discoveryEntry.discovery_payload.value_template =
`{{ value_json.${featurePropertyWithoutEndpoint(state)} }}`,
discoveryEntry.discovery_payload.state_open = 'OPEN';
@@ -539,9 +546,11 @@ export default class HomeAssistant extends Extension {
expose_pin: {entity_category: 'config', icon: 'mdi:pin'},
flip_indicator_light: {entity_category: 'config', icon: 'mdi:arrow-left-right'},
gas: {device_class: 'gas'},
indicator_mode: {entity_category: 'config', icon: 'mdi:led-on'},
invert_cover: {entity_category: 'config', icon: 'mdi:arrow-left-right'},
led_disabled_night: {entity_category: 'config', icon: 'mdi:led-off'},
led_indication: {entity_category: 'config', icon: 'mdi:led-on'},
led_enable: {entity_category: 'config', icon: 'mdi:led-on'},
legacy: {entity_category: 'config', icon: 'mdi:cog'},
motor_reversal: {entity_category: 'config', icon: 'mdi:arrow-left-right'},
moving: {device_class: 'moving'},
@@ -742,11 +751,19 @@ export default class HomeAssistant extends Extension {
discovery_payload: {
value_template: `{{ value_json.${firstExpose.property} }}`,
enabled_by_default: !allowsSet,
unit_of_measurement: firstExpose.unit || '',
...(firstExpose.unit && {unit_of_measurement: firstExpose.unit}),
...lookup[firstExpose.name],
...extraAttrs,
},
};
// When a device_class is set, unit_of_measurement must be set, otherwise warnings are generated.
// https://github.com/Koenkk/zigbee2mqtt/issues/15958#issuecomment-1377483202
if (discoveryEntry.discovery_payload.device_class &&
!discoveryEntry.discovery_payload.unit_of_measurement) {
delete discoveryEntry.discovery_payload.device_class;
}
discoveryEntries.push(discoveryEntry);
/**
@@ -1058,7 +1075,7 @@ export default class HomeAssistant extends Extension {
const updateStateSensor: DiscoveryEntry = {
type: 'sensor',
object_id: 'update_state',
mockProperties: [{property: 'update', value: {state: null}}],
mockProperties: [], // update is mocked below with updateSensor
discovery_payload: {
icon: 'mdi:update',
value_template: `{{ value_json['update']['state'] }}`,
@@ -1094,8 +1111,7 @@ export default class HomeAssistant extends Extension {
command_topic: `${settings.get().mqtt.base_topic}/bridge/request/device/ota_update/update`,
payload_install: `{"id": "${entity.ieeeAddr}"}`,
value_template: `{{ value_json['update']['installed_version'] }}`,
latest_version_template: `{% if value_json['update']['state'] == "available" %}{{ 'newer' }}` +
`{% else %}{{ value_json['update']['installed_version'] }}{% endif %}`,
latest_version_template: `{{ value_json['update']['latest_version'] }}`,
},
};
configs.push(updateSensor);
@@ -1200,7 +1216,10 @@ export default class HomeAssistant extends Extension {
payload.availability.push({topic: `${baseTopic}/availability`});
}
if (!settings.get().advanced.legacy_availability_payload) {
if (entity.isDevice() && entity.options.disabled) {
// Mark disabled device always as unavailable
payload.availability.forEach((a: KeyValue) => a.value_template = '{{ "offline" }}');
} else if (!settings.get().advanced.legacy_availability_payload) {
payload.availability.forEach((a: KeyValue) => a.value_template = '{{ value_json.state }}');
}
@@ -1461,8 +1480,8 @@ export default class HomeAssistant extends Extension {
}
}
if (entity.isDevice() && entity.definition?.ota && message.hasOwnProperty('update')) {
message['update']['installed_version'] = entity.zh.softwareBuildID || 'unknown';
if (entity.isDevice() && entity.definition?.ota && message.update?.latest_version == null) {
message.update = {...message.update, installed_version: -1, latest_version: -1};
}
}
+1 -1
View File
@@ -214,7 +214,7 @@ export default class NetworkMap extends Extension {
async networkScan(includeRoutes: boolean): Promise<Topology> {
logger.info(`Starting network scan (includeRoutes '${includeRoutes}')`);
const devices = this.zigbee.devices().filter((d) => d.zh.type !== 'GreenPower');
const devices = this.zigbee.devices().filter((d) => d.zh.type !== 'GreenPower' && !d.options.disabled);
const lqis: Map<Device, zh.LQI> = new Map();
const routingTables: Map<Device, zh.RoutingTable> = new Map();
const failed: Map<Device, string[]> = new Map();
+35 -22
View File
@@ -24,8 +24,13 @@ function isValidUrl(url: string): boolean {
type UpdateState = 'updating' | 'idle' | 'available';
interface UpdatePayload {
update_available?: boolean
// eslint-disable-next-line camelcase
update: {progress?: number, remaining?: number, state: UpdateState}, update_available?: boolean}
update: {
progress?: number, remaining?: number, state: UpdateState,
installed_version: number | null, latest_version: number | null
}
}
const legacyTopicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/ota_update/.+$`);
const topicRegex =
@@ -86,18 +91,18 @@ export default class OTAUpdate extends Extension {
if (!check) return;
this.lastChecked[data.device.ieeeAddr] = Date.now();
let available = false;
let availableResult: zhc.OtaUpdateAvailableResult = null;
try {
available = await data.device.definition.ota.isUpdateAvailable(data.device.zh, logger, data.data);
availableResult = await data.device.definition.ota.isUpdateAvailable(data.device.zh, logger, data.data);
} catch (e) {
supportsOTA = false;
logger.debug(`Failed to check if update available for '${data.device.name}' (${e.message})`);
}
const payload = this.getEntityPublishPayload(available ? 'available' : 'idle');
const payload = this.getEntityPublishPayload(data.device, availableResult ?? 'idle');
this.publishEntityState(data.device, payload);
if (available) {
if (availableResult?.available) {
const message = `Update available for '${data.device.name}'`;
logger.info(message);
@@ -113,11 +118,9 @@ export default class OTAUpdate extends Extension {
}
// Respond to the OTA request: respond with NO_IMAGE_AVAILABLE (0x98) (so the client stops requesting OTAs)
const endpoint = data.device.zh.endpoints.find((e) => e.supportsOutputCluster('genOta'));
if (endpoint) {
// Some devices send OTA requests without defining OTA cluster as input cluster.
await endpoint.commandResponse('genOta', 'queryNextImageResponse', {status: 0x98});
}
const endpoint = data.device.zh.endpoints.find((e) => e.supportsOutputCluster('genOta')) || data.endpoint;
await endpoint.commandResponse('genOta', 'queryNextImageResponse', {status: 0x98});
logger.debug(`Responded to OTA request of '${data.device.name}' with 'NO_IMAGE_AVAILABLE'`);
}
private async readSoftwareBuildIDAndDateCode(device: Device, sendWhen: 'active' | 'immediate'):
@@ -131,14 +134,22 @@ export default class OTAUpdate extends Extension {
}
}
private getEntityPublishPayload(state: UpdateState, progress: number=null, remaining: number=null): UpdatePayload {
const payload: UpdatePayload = {update: {state}};
private getEntityPublishPayload(device: Device, state: zhc.OtaUpdateAvailableResult | UpdateState,
progress: number=null, remaining: number=null): UpdatePayload {
const deviceUpdateState = this.state.get(device).update;
const payload: UpdatePayload = {update: {
state: typeof state === 'string' ? state : (state.available ? 'available' : 'idle'),
installed_version: typeof state === 'string' ?
deviceUpdateState?.installed_version : state.currentFileVersion,
latest_version: typeof state === 'string' ?
deviceUpdateState?.latest_version : state.otaFileVersion,
}};
if (progress !== null) payload.update.progress = progress;
if (remaining !== null) payload.update.remaining = Math.round(remaining);
/* istanbul ignore else */
if (this.legacyApi) {
payload.update_available = state === 'available';
payload.update_available = typeof state === 'string' ? state === 'available' : state.available;
}
return payload;
@@ -189,23 +200,24 @@ export default class OTAUpdate extends Extension {
}
try {
const available = await device.definition.ota.isUpdateAvailable(device.zh, logger);
const msg = `${available ? 'Update' : 'No update'} available for '${device.name}'`;
const availableResult = await device.definition.ota.isUpdateAvailable(device.zh, logger);
const msg = `${availableResult.available ? 'Update' : 'No update'} available for '${device.name}'`;
logger.info(msg);
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const meta = {status: available ? 'available' : 'not_available', device: device.name};
const meta = {
status: availableResult.available ? 'available' : 'not_available', device: device.name};
this.mqtt.publish(
'bridge/log',
stringify({type: `ota_update`, message: msg, meta}),
);
}
const payload = this.getEntityPublishPayload(available ? 'available' : 'idle');
const payload = this.getEntityPublishPayload(device, availableResult);
this.publishEntityState(device, payload);
this.lastChecked[device.ieeeAddr] = Date.now();
responseData.updateAvailable = available;
responseData.updateAvailable = availableResult.available;
} catch (e) {
error = `Failed to check if update available for '${device.name}' (${e.message})`;
errorStack = e.stack;
@@ -241,7 +253,7 @@ export default class OTAUpdate extends Extension {
logger.info(msg);
const payload = this.getEntityPublishPayload('updating', progress, remaining);
const payload = this.getEntityPublishPayload(device, 'updating', progress, remaining);
this.publishEntityState(device, payload);
/* istanbul ignore else */
@@ -252,11 +264,12 @@ export default class OTAUpdate extends Extension {
};
const from_ = await this.readSoftwareBuildIDAndDateCode(device, 'immediate');
await device.definition.ota.updateToLatest(device.zh, logger, onProgress);
const fileVersion = await device.definition.ota.updateToLatest(device.zh, logger, onProgress);
logger.info(`Finished update of '${device.name}'`);
this.eventBus.emitReconfigure({device});
this.removeProgressAndRemainingFromState(device);
const payload = this.getEntityPublishPayload('idle');
const payload = this.getEntityPublishPayload(device,
{available: false, currentFileVersion: fileVersion, otaFileVersion: fileVersion});
this.publishEntityState(device, payload);
const to = await this.readSoftwareBuildIDAndDateCode(device, 'active');
const [fromS, toS] = [stringify(from_), stringify(to)];
@@ -276,7 +289,7 @@ export default class OTAUpdate extends Extension {
errorStack = e.stack;
this.removeProgressAndRemainingFromState(device);
const payload = this.getEntityPublishPayload('available');
const payload = this.getEntityPublishPayload(device, 'available');
this.publishEntityState(device, payload);
/* istanbul ignore else */
+2 -1
View File
@@ -1,6 +1,7 @@
import * as settings from '../util/settings';
import zigbeeHerdsmanConverters from 'zigbee-herdsman-converters';
import philips from 'zigbee-herdsman-converters/lib/philips';
import logger from '../util/logger';
import utils from '../util/utils';
import Extension from './extension';
@@ -18,7 +19,7 @@ const sceneConverterKeys = ['scene_store', 'scene_add', 'scene_remove', 'scene_r
const defaultGroupConverters = [
zigbeeHerdsmanConverters.toZigbeeConverters.light_onoff_brightness,
zigbeeHerdsmanConverters.toZigbeeConverters.light_color_colortemp,
zigbeeHerdsmanConverters.toZigbeeConverters.effect,
philips.tz.effect, // Support Hue effects for groups
zigbeeHerdsmanConverters.toZigbeeConverters.ignore_transition,
zigbeeHerdsmanConverters.toZigbeeConverters.cover_position_tilt,
zigbeeHerdsmanConverters.toZigbeeConverters.thermostat_occupied_heating_setpoint,
+7 -4
View File
@@ -17,10 +17,10 @@ class State {
private state: {[s: string | number]: KeyValue} = {};
private file = data.joinPath('state.json');
private timer: NodeJS.Timer = null;
private eventBus: EventBus;
constructor(eventBus: EventBus) {
constructor(private readonly eventBus: EventBus, private readonly zigbee: Zigbee) {
this.eventBus = eventBus;
this.zigbee = zigbee;
}
start(): void {
@@ -28,11 +28,14 @@ class State {
// Save the state on every interval
this.timer = setInterval(() => this.save(), saveInterval);
this.eventBus.onDeviceLeave(this, (data) => delete this.state[data.ieeeAddr]);
}
stop(): void {
this.eventBus.removeListeners(this);
// Remove any invalid states (ie when the device has left the network) when the system is stopped
Object.keys(this.state)
.filter((k) => typeof k === 'string' && !this.zigbee.resolveEntity(k)) // string key = ieeeAddr
.forEach((k) => delete this.state[k]);
clearTimeout(this.timer);
this.save();
}
+8 -2
View File
@@ -104,6 +104,8 @@ declare global {
access: number, property: string, unit?: string,
value_min?: number, value_max?: number}
interface OtaUpdateAvailableResult {available: boolean, currentFileVersion: number, otaFileVersion: number}
interface Definition {
model: string,
zigbeeModel: string[],
@@ -120,9 +122,10 @@ declare global {
onEvent?: (type: string, data: KeyValue, device: zh.Device,
settings: KeyValue, state: KeyValue) => Promise<void>;
ota?: {
isUpdateAvailable: (device: zh.Device, logger: Logger, data?: KeyValue) => Promise<boolean>;
isUpdateAvailable: (device: zh.Device, logger: Logger, data?: KeyValue)
=> Promise<OtaUpdateAvailableResult>;
updateToLatest: (device: zh.Device, logger: Logger,
onProgress: (progress: number, remaining: number) => void) => Promise<void>;
onProgress: (progress: number, remaining: number) => void) => Promise<number>;
}
}
@@ -233,6 +236,8 @@ declare global {
host?: string,
port?: number,
url?: string,
ssl_cert?: string,
ssl_key?: string,
},
devices?: {[s: string]: DeviceOptions},
groups?: {[s: string]: GroupOptions},
@@ -274,6 +279,7 @@ declare global {
interface DeviceOptions {
ID?: string,
disabled?: boolean,
retention?: number,
availability?: boolean | {timeout: number},
optimistic?: boolean,
+4
View File
@@ -16,3 +16,7 @@ declare module 'zigbee-herdsman-converters/lib/ota/zigbeeOTA' {
export function useIndexOverride(indexFileName: string): void;
export function setDataDir(dataDir: string): void;
}
declare module 'zigbee-herdsman-converters/lib/philips' {
export const tz: {[s: string]: zhc.ToZigbeeConverter};
}
+20 -2
View File
@@ -388,6 +388,18 @@
"title": "URL",
"description": "URL on which the frontend can be reached, currently only used for the Home Assistant device configuration page",
"requiresRestart": true
},
"ssl_cert" : {
"type": ["string", "null"],
"title": "Certificate file path",
"description": "SSL Certificate file path for exposing HTTPS. The sibling property 'ssl_key' must be set for HTTPS to be activated.",
"requiresRestart": true
},
"ssl_key" : {
"type": ["string", "null"],
"title": "key file path",
"description": "SSL key file path for exposing HTTPS. The sibling property 'ssl_cert' must be set for HTTPS to be activated.",
"requiresRestart": true
}
}
}
@@ -499,13 +511,13 @@
"type": "number",
"title": "Port",
"description": "The port on the host that syslog is running on, defaults to syslogd's default port.",
"default": 123
"default": 514
},
"protocol": {
"type": "string",
"title": "Protocol",
"description": "The network protocol to log over (e.g. tcp4, udp4, tls4, unix, unix-connect, etc).",
"default": "tcp4",
"default": "udp4",
"examples": [
"udp4",
"tls4",
@@ -782,6 +794,12 @@
"title": "Retain",
"description": "Retain MQTT messages of this device"
},
"disabled": {
"type": "boolean",
"title": "Disabled",
"description": "Disables the device (excludes device from network scans, availability and group state updates)",
"requiresRestart": true
},
"retention": {
"type": "number",
"title": "Retention",
+14 -5
View File
@@ -3,7 +3,7 @@ import utils from './utils';
import objectAssignDeep from 'object-assign-deep';
import path from 'path';
import yaml from './yaml';
import Ajv from 'ajv';
import Ajv, {ValidateFunction} from 'ajv';
import schemaJson from './settings.schema.json';
export let schema = schemaJson;
// @ts-ignore
@@ -31,7 +31,10 @@ const file = process.env.ZIGBEE2MQTT_CONFIG ?? data.joinPath('configuration.yaml
const ajvSetting = new Ajv({allErrors: true}).addKeyword('requiresRestart').compile(schemaJson);
const ajvRestartRequired = new Ajv({allErrors: true})
.addKeyword({keyword: 'requiresRestart', validate: (s: unknown) => !s}).compile(schemaJson);
const ajvRestartRequiredDeviceOptions = new Ajv({allErrors: true})
.addKeyword({keyword: 'requiresRestart', validate: (s: unknown) => !s}).compile(schemaJson.definitions.device);
const ajvRestartRequiredGroupOptions = new Ajv({allErrors: true})
.addKeyword({keyword: 'requiresRestart', validate: (s: unknown) => !s}).compile(schemaJson.definitions.group);
const defaults: RecursivePartial<Settings> = {
permit_join: false,
external_converters: [],
@@ -227,7 +230,7 @@ function write(): void {
// Write devices/groups to separate file if required.
const writeDevicesOrGroups = (type: 'devices' | 'groups'): void => {
if (typeof actual[type] === 'string' || Array.isArray(actual[type])) {
if (typeof actual[type] === 'string' || (Array.isArray(actual[type]) && actual[type].length > 0)) {
const fileToWrite = Array.isArray(actual[type]) ? actual[type][0] : actual[type];
const content = objectAssignDeep({}, settings[type]);
@@ -358,7 +361,7 @@ function read(): Settings {
// Read devices/groups configuration from separate file if specified.
const readDevicesOrGroups = (type: 'devices' | 'groups'): void => {
if (typeof s[type] === 'string' || Array.isArray(s[type])) {
if (typeof s[type] === 'string' || (Array.isArray(s[type]) && Array(s[type]).length > 0)) {
/* eslint-disable-line */ // @ts-ignore
const files: string[] = Array.isArray(s[type]) ? s[type] : [s[type]];
s[type] = {};
@@ -667,21 +670,27 @@ export function removeGroup(IDorName: string | number): void {
write();
}
export function changeEntityOptions(IDorName: string, newOptions: KeyValue): void {
export function changeEntityOptions(IDorName: string, newOptions: KeyValue): boolean {
const settings = getInternalSettings();
delete newOptions.friendly_name;
delete newOptions.devices;
let validator: ValidateFunction;
if (getDevice(IDorName)) {
objectAssignDeep(settings.devices[getDevice(IDorName).ID], newOptions);
utils.removeNullPropertiesFromObject(settings.devices[getDevice(IDorName).ID]);
validator = ajvRestartRequiredDeviceOptions;
} else if (getGroup(IDorName)) {
objectAssignDeep(settings.groups[getGroup(IDorName).ID], newOptions);
utils.removeNullPropertiesFromObject(settings.groups[getGroup(IDorName).ID]);
validator = ajvRestartRequiredGroupOptions;
} else {
throw new Error(`Device or group '${IDorName}' does not exist`);
}
write();
validator(newOptions);
const restartRequired = validator.errors && !!validator.errors.find((e) => e.keyword === 'requiresRestart');
return restartRequired;
}
export function changeFriendlyName(IDorName: string, newName: string): void {
+40 -3
View File
@@ -4,6 +4,8 @@ import data from './data';
import vm from 'vm';
import fs from 'fs';
import path from 'path';
import {detailedDiff} from 'deep-object-diff';
import objectAssignDeep from 'object-assign-deep';
// construct a local ISO8601 string (instead of UTC-based)
// Example:
@@ -132,8 +134,8 @@ function parseJSON(value: string, fallback: string): KeyValue | string {
}
}
function loadModuleFromText(moduleCode: string): unknown {
const moduleFakePath = path.join(__dirname, 'externally-loaded.js');
function loadModuleFromText(moduleCode: string, name?: string): unknown {
const moduleFakePath = path.join(__dirname, '..', '..', 'data', 'extension', name || 'externally-loaded.js');
const sandbox = {
require: require,
module: {},
@@ -295,6 +297,8 @@ function isAvailabilityEnabledForEntity(entity: Device | Group, settings: Settin
const enabledGlobal = settings.advanced.availability_timeout || settings.availability;
if (!enabledGlobal) return false;
if (entity.isDevice() && entity.options.disabled) return false;
const passlist = settings.advanced.availability_passlist.concat(settings.advanced.availability_whitelist);
if (passlist.length > 0) {
return passlist.includes(entity.name) || passlist.includes(entity.ieeeAddr);
@@ -363,11 +367,44 @@ function clone(obj: KeyValue): KeyValue {
return JSON.parse(JSON.stringify(obj));
}
function computeSettingsToChange(current: KeyValue, new_: KeyValue): KeyValue {
const diff: KeyValue = detailedDiff(current, new_);
// Remove any settings that are in the deleted.diff but not in the passed options
const cleanupDeleted = (options: KeyValue, deleted: KeyValue): void => {
for (const key of Object.keys(deleted)) {
if (!(key in options)) {
delete deleted[key];
} else if (!Array.isArray(options[key])) {
cleanupDeleted(options[key], deleted[key]);
}
}
};
cleanupDeleted(new_, diff.deleted);
// objectAssignDeep requires object prototype which is missing from detailedDiff, therefore clone
const newSettings = objectAssignDeep({}, clone(diff.added), clone(diff.updated), clone(diff.deleted));
// deep-object-diff converts arrays to objects, set original array back here
const convertBackArray = (before: KeyValue, after: KeyValue): void => {
for (const [key, afterValue] of Object.entries(after)) {
const beforeValue = before[key];
if (Array.isArray(beforeValue)) {
after[key] = beforeValue;
} else if (afterValue && typeof beforeValue === 'object') {
convertBackArray(beforeValue, afterValue);
}
}
};
convertBackArray(new_, newSettings);
return newSettings;
}
export default {
endpointNames, capitalize, getZigbee2MQTTVersion, getDependencyVersion, formatDate, objectHasProperties,
equalsPartial, getObjectProperty, getResponse, parseJSON, loadModuleFromText, loadModuleFromFile,
getExternalConvertersDefinitions, removeNullPropertiesFromObject, toNetworkAddressHex, toSnakeCase,
parseEntityID, isEndpoint, isZHGroup, hours, minutes, seconds, validateFriendlyName, sleep,
sanitizeImageParameter, isAvailabilityEnabledForEntity, publishLastSeen, availabilityPayload,
getAllFiles, filterProperties, flatten, arrayUnique, clone,
getAllFiles, filterProperties, flatten, arrayUnique, clone, computeSettingsToChange,
};
+1564 -2030
View File
File diff suppressed because it is too large Load Diff
+20 -20
View File
@@ -1,6 +1,6 @@
{
"name": "zigbee2mqtt",
"version": "1.28.3",
"version": "1.30.2",
"description": "Zigbee to MQTT bridge using Zigbee-herdsman",
"main": "index.js",
"repository": {
@@ -35,7 +35,7 @@
},
"homepage": "https://koenkk.github.io/zigbee2mqtt",
"dependencies": {
"ajv": "^8.11.2",
"ajv": "^8.12.0",
"bind-decorator": "^1.0.11",
"connect-gzip-static": "2.1.1",
"debounce": "^1.2.1",
@@ -43,7 +43,7 @@
"fast-deep-equal": "^3.1.3",
"finalhandler": "^1.2.0",
"git-last-commit": "^1.0.1",
"humanize-duration": "^3.27.3",
"humanize-duration": "^3.28.0",
"js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"jszip": "^3.10.1",
@@ -51,40 +51,40 @@
"moment": "^2.29.4",
"mqtt": "4.3.7",
"object-assign-deep": "^0.4.0",
"rimraf": "^3.0.2",
"rimraf": "^4.1.2",
"semver": "^7.3.8",
"source-map-support": "^0.5.21",
"uri-js": "^4.4.1",
"winston": "^3.8.2",
"winston-syslog": "^2.7.0",
"winston-transport": "^4.5.0",
"ws": "^8.11.0",
"zigbee-herdsman": "0.14.76",
"zigbee-herdsman-converters": "14.0.680",
"zigbee2mqtt-frontend": "0.6.119"
"ws": "^8.12.1",
"zigbee-herdsman": "0.14.96",
"zigbee-herdsman-converters": "15.0.58",
"zigbee2mqtt-frontend": "0.6.125"
},
"devDependencies": {
"@babel/core": "^7.20.2",
"@babel/plugin-proposal-decorators": "^7.20.2",
"@babel/core": "^7.21.0",
"@babel/plugin-proposal-decorators": "^7.21.0",
"@babel/preset-env": "^7.20.2",
"@babel/preset-typescript": "^7.18.6",
"@babel/preset-typescript": "^7.21.0",
"@types/debounce": "^1.2.1",
"@types/finalhandler": "^1.2.0",
"@types/humanize-duration": "^3.27.1",
"@types/jest": "^29.2.3",
"@types/jest": "^29.4.0",
"@types/js-yaml": "^4.0.5",
"@types/object-assign-deep": "^0.4.0",
"@types/rimraf": "^3.0.2",
"@types/ws": "^8.5.3",
"@typescript-eslint/eslint-plugin": "^5.44.0",
"@typescript-eslint/parser": "^5.44.0",
"babel-jest": "^29.3.1",
"eslint": "^8.28.0",
"@types/ws": "^8.5.4",
"@typescript-eslint/eslint-plugin": "^5.53.0",
"@typescript-eslint/parser": "^5.53.0",
"babel-jest": "^29.4.3",
"eslint": "^8.34.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-jest": "^27.1.6",
"jest": "^29.3.1",
"eslint-plugin-jest": "^27.2.1",
"jest": "^29.4.3",
"tmp": "^0.2.1",
"typescript": "^4.9.3"
"typescript": "^4.9.5"
},
"jest": {
"coverageThreshold": {
+3 -2
View File
@@ -5,7 +5,8 @@ const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
for (const line of text.split('\n')) {
const model = zigbeeHerdsmanConverters.devices.find((d) => d.model === line);
const [modelNumber, user] = line.split(',').map((t) => t.trim());
const model = zigbeeHerdsmanConverters.devices.find((d) => d.model === modelNumber);
if (!model) throw new Error(`${line} does not exist`);
console.log(`- \`${line}\` ${model.vendor} ${model.description}`);
console.log(`- \`${modelNumber}\` ${model.vendor} ${model.description} (${user})`);
}
+31
View File
@@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIUU70Q1tbGP071yeQ8EcHd/0rxtnwwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMjEyMDMwMDA1MTBaFw0yMzAx
MDIwMDA1MTBaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQDR6bYQPgkfWlGQq19U2nMsAo0RZR7wSUuFS0MFpbQS
2Rr5x/4FwwKhTMOlfr6H2Xo/5pQvGOlhFE69j1HxQRooWDAswnscMv/9pb+BUqoE
7J29SEndIpmCaI0c3ejZsu4Nq8DxGod/RsQhJdbDU+hkw8qARJ0JjnTb+aN1KH8g
J3KeSMTOL3guBVZQujO0Unbjv47yBIFwi0IihR18aoRaxinb94Aqf92HcjZN++WU
gq7/2Qlm3tF5uJwuNtG/DmzReSblRcUgzPri7qIhb9z51gFNdNX6cTH3yNoF/KtT
XwEArvvsAyMCYuC6w8KI7WNmPI6yxSksgHdcNPlUVZYquLD+z2kYAdIByiyHmIpm
OH3jVN7lRJ6xITOGmyNktfRuSUi2GzECAOWIGpDD/5/gK1pacbOXpJWqVrTjUIAN
BrQf496CbabVgwaQEvq1QJJbuMbpma21/IlS6NpZS7vm/q1ojNsbEB6PYkSsBTzP
iZiiaomjhV2w5yIp5Zn8xyMFVKLb+SEDQBtZZ6CErmbgqp4B3U2b4k5LippLmZZK
pSk7BOuMpsZK8FIGS4KlANFfGVs79y6N+Jxue2IO9pRcpcpbqOXty4662BsPKInw
dKxKTr6TcTe0gEq1fpf1npYwcZ9t36LMJ7L51TbaaJ5sEsXe5QNOOOUgSwPqWmrE
lwIDAQABo1MwUTAdBgNVHQ4EFgQUYD/x9cCuGJgLUPkYLIVa8uvseBkwHwYDVR0j
BBgwFoAUYD/x9cCuGJgLUPkYLIVa8uvseBkwDwYDVR0TAQH/BAUwAwEB/zANBgkq
hkiG9w0BAQsFAAOCAgEAI+gp+8qqO4anj8NHgjJvXhrETLPJIeLuBp/cjF9HA2IL
oAd5rXWPnQ6qgh3q/HazILgIHwI0wSrhVtRlprgGIxNa59cD3RcZCtqFSWIqhAqZ
JnAye3bnGU/KahazQX3oP+y564b/QZvwpJDKC1EoMv1wdpXND2NVn4HwaBc7Tu/d
SX/Tr6k+e32K8mwE+00/yf2rLrmWmNjWtP0f7zRmH3j1+ovktk7UoVhjKXfhqMUE
pWdhPZIXQJ65JNMGKdeFI2ULQY9IbXHeWQUgBCyhTciMvm2XZzopk7rxrh9NZopS
pAlCrWBkOZrs6hfZrus9FOzwSkeIEY7NnhNk05kslERJxjkoFytp7mDd4mTKCVW0
dKc7G1hZWYfO3oe0Zjxi0ragyReNKtyL1BqMbDKJmxHl91iPbdueWix1xxhNFakH
l3eOzfa/DEixvzxy3hzCcvagNb5RsXFVMQXQTgnIW/5GcG5VVB2/o7KidPL3XtqR
Hc/0ebQvloGXgqJbyTXG9EInkwB35wXDQuHgwt1PhwzP1yfp2BCvDsQWrR3IxnBT
DO1o2RpsurgCgUpd0vcW17DePLNfprrhhU2uTPn2bNcWdT/rZmEImkNaA29Rku+3
SZQJZWIx4s2FMdVdAv6ZauROStTY/WayudfcFfP+hYvJ5/ccQ9Gkb7vrGUssFRw=
-----END CERTIFICATE-----
+54
View File
@@ -0,0 +1,54 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIJnDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIrNH7wPLdpbUCAggA
MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECN97bYsRcF6xBIIJSLVWpqTzdlNO
5J7kOrNcdbbg+SSEYbtT+G9gtwtkI8KO+xzYQZ/fdqmXgNqf9AQmikwDWQkoS4gC
qq6S+iKYn7Y8ZNZ/xu7gWW6L6uNZ3LTe/vkpenU6kmZrDysFTW5b7iH2VmMc/2h/
J3o3unxeMLMVSaoDG3MG08qj53yTmetTkZZZc4fgf6vK9/4fTJhdXu6n+1COBLBI
KbqbTRKnai3CNiYzz3UHyp9CGx1nX06beTJW8bYJUld9cJz+ozLNSV78oCkcUfwG
yR7p/6+86cWor5SwYG1wEm6w2bXrIJILTUdT92gXC8fhc/B6Id8hcuLIHcvFQ/CP
31y4Qwea3vY6b1TeRbqS4/fYOYajsiZi9ps0Meg55qMJj/UuNW2iLlR7PVsDXmPO
33X6cpEVyIlNCJzRREotzSoGmcXjQ5Dgd1LkxZPb163NGkRoJT4GcKCtQxYIyIzy
qyU0YF7I5LPKtNt1IPURHHerW42jM2jFlISxUixgl5Pj3WrmXeEmXq7htItn6X/q
tVEVUf6srs3aos2Ohx7Ji9PVDNuMGGGKxleCM6fU9ax/gYuk5r6PMYGPsD26xuZ0
A9WbFFFI6N1V+M3YsJJdxevZRV1NB3RymYGwFoZFDOKLb5DYJmPP3VVN/DsMNPkw
qaue7rinxBdLi8J6Itusiscg5pCyYQaujXtkK/bi6c1HCld4RvWCDgeU52SWRdJ9
2Np2629AeBvBfHP1/DsaUsyQPJAgDJk0HXoJyqstoALkRZ6ICoi1833LW/JGIWOv
xnl3JZ2q12aqb2OWxiBUKspnt2y5C2KpQJdmuEL68lAKLg+fzLi/jhJG1bwzWo79
I5G+QeV9+kI3vAIYHu331a+Yevs2p84sIumDGFSCSHAaWMmsrizJ4eKl5NnwZHS+
xUvcW+ISRKNRm4DWAL/nlwBJWMhCz+nsTKYvE1JO/IH2P5xxtD6bTjJ3YkVAHkBZ
wfApCBxdiQurEiabj06C4OCcNRkqJ/GwLrdxiwTCAt2hd7Dn+ttLo3Fi6iPRae8c
ZtWsCWaFObZ1QBeu7lKm+ev/Ab2SHtp6QMfrArNsrdIxSH1+Sm4DgfHmTmte2Tse
KFHy5Rwokst+Y18zjp3auJ3+NTTr9D1x2JCdQ6wkh66Pyd2FBLqRrMI1fHq9t9P+
r4DQm9I6FbgALm9yJ2QxV4ytwFGsowAye3vF+hfO5OjAScrG5DrxASRXlnp59zpd
+vMLk6rzcfiHzaJzYc/SOU9ubKN93aJmlL0Zgf1gg54oGnmPbEJeqQzeKVBtisro
FrNUPEFx5QOaqjVipPOpZfpTb0oaUpBdp4RpeI7W3Gb/dsmsRj7ItxvM6QxFdd5e
XfDZEYaUJRwSG3lHg9URWwSmfRWV81BhEuwvji4rNIEQ32VMsi2WWWPLOYYKGIw/
yE56xWBw7K78XwVx/b5yoNHBNjVNyjpNZ7jXkx6zdOjbqEpXJP+WfgVkYwlhlvAq
cCDbrF7jVWFqTV9tZNxlbaFlS8f17vld8DmmJ29Lo+WecsOo3M9n2Z6i79u7Xg2O
ip0V2Textq8rrKOwC1TLCRoQLypENO/cwdaF5amVkoXYDlY0rQtxf65Xe9bnJLrM
1G+tG8NZQu/bvW0mgrm0XHt2i6yfjn6sXEOY5VEC2+gRUriQ+GCETgzMe6ukb26/
VT38Va10SIu+xXwf/GXTwLskOO0AyWLBopTpL5q6faM4o/USX+Xl98sQVNQurHMS
xSQtpL6YPhQrmW7q5Q57OVWy70kEU8RpOJx6I7uyoUDz2g3bjmpIPpQX55vhofXK
3Xn76Ce9TyiVol71mPtBT1tYzi3ytZh/EYm2Wq3lEhbMT7zjw3T1Ypb4+q12VdCy
0Y9swKsQdhRCT6FwcCFSpCc2TzCCJCR6mChTe+z6rNm5BS2M6BaYcrX8PP82rZ8k
L0l3ymSLqSJ3J6Q+fdKhRq8EbCq6ngi31Q/l7fPHkW5VAJyowPRayD7UM09n6ou+
sg/kfQB8nh46uVxRRDpyUsrfhtIEQec2xrU17NmCr2VlAeOjWBn8lE/J1Gfn8WyV
05mY1aDGcRk/x02+74UujyuozYMfIVDXHpxp8YteTnBfiKVrRq+Aa9zVX9bkyagp
SJUTpF7lqMmiuvyZnG2xfLDfILR/zTRIRrm+ZIJLHeVQJtibOJ1llxE6kRvKx6zp
La47YCHKH+rLDTlNgrE5K/JtQgOKc+7bGQ6SCszf8OZWi5B2+Z+Ht36c3xQk9IOW
cjNpvjZxpiw6L1MhlQPE/wxz2JkOlKjJN0MmLscIyu15X+W/BvHwB8d11CRKUUiE
pV7OUoW2Ol1IgFHrEY33IhxwtyKtAKfQCiORarnwk/GvcD3TqmnzUeQdscdww4BI
wsO3c3aGr+wvU3P/8oFAoffD1KRihtoZ6CB347JcnaeEPlOS13a28JsJCOtjMxy9
wBH6UgzTwo04lDAStST+aIT4TaU4dlWmsAEQTpE8sa4DjpFtzK/ll+Wg2sA/SYaA
uAugsAOnTb1pkgk43GoYfc2fZCu1g9b/L3mZrE62W8HDfm67PWT65XMJziy/hcCy
DbXMAWfohLdePKDG7J0QQI8xcTN9TT5q3ZCglI9Xf7PE/qrHROdRgF8c8zyvB9dG
AVx96Z7akBoveqlFS44MnxIOGI0AzV8MrVbN51W6RHNyg/6UTL50/8zHmRWZj+iO
tfqiW+CtSHacYPy+Tcuft30PWZZkQDGphbRxaBRB00Fb+Bo57epDw4JS+4wRru33
2RHaVzQmnXwR6kP85x4uAR53UdgMgfK4cyylnFqGDlLFrHZfauFHg3kMIwqgVIVA
uMRrWC9XXU11j6NnP9J6Y64uSrjsh6eZOrtJIwA16U2r3PMMbn1hIdADrKu1MsPt
bkNsnPQUKJyX8DbRX45kje5ssUerbkxhvewvHQJlTyDxtgk/pGHOyKXoT+5vQEj9
pwHOrXi+QWidzi2vfRoNQJ0wXJr39dDTn0hVdsgFo2PsTh98W9cocVQfJnCL4Djs
Zlsp/BJOwlb2e3Q28E6pNyT+N63ZYEO9zLV/WmCB/eV2uMVoKibX3KszYkn0jAs0
RBytgnB8YW3n9RyorShchCKJzdjzR7nw79/lSLgDFE6W6Xh3VaGM07hz0vYWfaui
yvytkM9LPan2/GJM4zLFeA==
-----END ENCRYPTED PRIVATE KEY-----
+9
View File
@@ -179,6 +179,15 @@ describe('Availability', () => {
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1);
});
it('Should not ping disabled devices', async () => {
settings.set(['devices', devices.bulb_color.ieeeAddr, 'disabled'], true);
await resetExtension();
MQTT.publish.mockClear();
await advancedTime(utils.minutes(15));
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
});
it('Should allow to change availability timeout via avaiability options', async () => {
settings.set(['availability'], {active: {timeout: 30}});
await resetExtension();
+32 -5
View File
File diff suppressed because one or more lines are too long
+14 -1
View File
@@ -65,6 +65,7 @@ describe('Configure', () => {
jest.useFakeTimers();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await jest.runOnlyPendingTimers();
await flushPromises();
});
@@ -72,8 +73,9 @@ describe('Configure', () => {
data.writeDefaultConfiguration();
settings.reRead();
mocksClear.forEach((m) => m.mockClear());
coordinatorEndpoint = zigbeeHerdsman.devices.coordinator.getEndpoint(1);
coordinatorEndpoint = zigbeeHerdsman.devices.coordinator.getEndpoint(1);
await resetExtension();
await jest.runOnlyPendingTimers();
});
afterAll(async () => {
@@ -100,6 +102,17 @@ describe('Configure', () => {
expectBulbConfigured();
});
it('Should not re-configure disabled devices', async () => {
expectBulbConfigured();
const device = zigbeeHerdsman.devices.bulb;
await flushPromises();
mockClear(device);
settings.set(['devices', device.ieeeAddr, 'disabled'], true);
zigbeeHerdsman.events.deviceJoined({device});
await flushPromises();
expectBulbNotConfigured();
});
it('Should reconfigure reporting on reconfigure event', async () => {
expectBulbConfigured();
const device = controller.zigbee.resolveEntity(zigbeeHerdsman.devices.bulb);
+9 -1
View File
@@ -147,7 +147,6 @@ describe('Controller', () => {
logger.error.mockClear();
controller.mqtt.client.reconnecting = true;
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
expect(logger.error).toHaveBeenCalledWith("Not connected to MQTT server!");
controller.mqtt.client.reconnecting = false;
});
@@ -719,4 +718,13 @@ describe('Controller', () => {
await flushPromises();
expect(logger.debug).toHaveBeenCalledWith(`Received Zigbee message from 'Coordinator', type 'attributeReport', cluster 'genBasic', data '{"modelId":null}' from endpoint 1, ignoring since it is from coordinator`);
});
it('Should remove state of removed device when stopped', async () => {
await controller.start();
const device = controller.zigbee.resolveEntity('bulb');
expect(controller.state.state[device.ieeeAddr]).toStrictEqual({"brightness":50,"color_temp":370,"linkquality":99,"state":"ON"});
device.zh.isDeleted = true;
await controller.stop();
expect(controller.state.state[device.ieeeAddr]).toStrictEqual(undefined);
});
});
+60 -3
View File
@@ -7,6 +7,7 @@ const Controller = require('../lib/controller');
const stringify = require('json-stable-stringify-without-jsonify');
const flushPromises = require('./lib/flushPromises');
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
const path = require("path");
jest.spyOn(process, 'exit').mockImplementation(() => {});
const mockHTTP = {
@@ -19,6 +20,16 @@ const mockHTTP = {
events: {},
};
const mockHTTPS = {
implementation: {
listen: jest.fn(),
on: (event, handler) => {mockHTTPS.events[event] = handler},
close: jest.fn().mockImplementation((cb) => cb()),
},
variables: {},
events: {},
};
const mockWSocket = {
close: jest.fn(),
};
@@ -50,6 +61,13 @@ jest.mock('http', () => ({
}),
}));
jest.mock('https', () => ({
createServer: jest.fn().mockImplementation((onRequest) => {
mockHTTPS.variables.onRequest = onRequest;
return mockHTTPS.implementation;
}),
}));
jest.mock("connect-gzip-static", () =>
jest.fn().mockImplementation((path) => {
mockNodeStatic.variables.path = path
@@ -98,7 +116,6 @@ describe('Frontend', () => {
await controller.start();
expect(mockNodeStatic.variables.path).toBe("my/dummy/path");
expect(mockHTTP.implementation.listen).toHaveBeenCalledWith(8081, "127.0.0.1");
const mockWSClient = {
implementation: {
terminate: jest.fn(),
@@ -111,6 +128,46 @@ describe('Frontend', () => {
expect(mockWSClient.implementation.terminate).toHaveBeenCalledTimes(1);
expect(mockHTTP.implementation.close).toHaveBeenCalledTimes(1);
expect(mockWS.implementation.close).toHaveBeenCalledTimes(1);
mockHTTP.implementation.listen.mockClear();
mockHTTPS.implementation.listen.mockClear();
});
it('Start/stop HTTPS valid', async () => {
settings.set(['frontend','ssl_cert'], path.join(__dirname,'assets','certs','dummy.crt'));
settings.set(['frontend','ssl_key'], path.join(__dirname,'assets','certs','dummy.key'));
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
expect(mockHTTP.implementation.listen).not.toHaveBeenCalledWith(8081, "127.0.0.1");
expect(mockHTTPS.implementation.listen).toHaveBeenCalledWith(8081, "127.0.0.1");
await controller.stop();
mockHTTP.implementation.listen.mockClear();
mockHTTPS.implementation.listen.mockClear();
});
it('Start/stop HTTPS invalid : missing config', async () => {
settings.set(['frontend','ssl_cert'], path.join(__dirname,'assets','certs','dummy.crt'));
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
expect(mockHTTP.implementation.listen).toHaveBeenCalledWith(8081, "127.0.0.1");
expect(mockHTTPS.implementation.listen).not.toHaveBeenCalledWith(8081, "127.0.0.1");
await controller.stop();
mockHTTP.implementation.listen.mockClear();
mockHTTPS.implementation.listen.mockClear();
});
it('Start/stop HTTPS invalid : missing file', async () => {
settings.set(['frontend','ssl_cert'], 'filesNotExists.crt');
settings.set(['frontend','ssl_key'], path.join(__dirname,'assets','certs','dummy.key'));
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
expect(mockHTTP.implementation.listen).toHaveBeenCalledWith(8081, "127.0.0.1");
expect(mockHTTPS.implementation.listen).not.toHaveBeenCalledWith(8081, "127.0.0.1");
await controller.stop();
mockHTTP.implementation.listen.mockClear();
mockHTTPS.implementation.listen.mockClear();
});
it('Websocket interaction', async () => {
@@ -140,7 +197,7 @@ describe('Frontend', () => {
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb_color',
stringify({state: 'ON', power_on_behavior:null, linkquality: null, update_available: null, update: {state: null, installed_version: "unknown"}}),
stringify({state: 'ON', power_on_behavior:null, linkquality: null, update_available: null, update: {state: null, installed_version: -1, latest_version: -1}}),
{ retain: false, qos: 0 },
expect.any(Function)
);
@@ -151,7 +208,7 @@ describe('Frontend', () => {
// Received message on socket
expect(mockWSClient.implementation.send).toHaveBeenCalledTimes(1);
expect(mockWSClient.implementation.send).toHaveBeenCalledWith(stringify({topic: 'bulb_color', payload: {state: 'ON', power_on_behavior:null, linkquality: null, update_available: null, update: {state: null, installed_version: "unknown"}}}));
expect(mockWSClient.implementation.send).toHaveBeenCalledWith(stringify({topic: 'bulb_color', payload: {state: 'ON', power_on_behavior:null, linkquality: null, update_available: null, update: {state: null, installed_version: -1, latest_version: -1}}}));
// Shouldnt set when not ready
mockWSClient.implementation.send.mockClear();
+16
View File
@@ -334,6 +334,22 @@ describe('Groups', () => {
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_1", stringify({"state":"ON"}), {"retain": false, qos: 0}, expect.any(Function));
});
it('Should not publish state change when group changes state and device is disabled', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['devices', device.ieeeAddr, 'disabled'], true);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: [device.ieeeAddr]}});
await resetExtension();
MQTT.publish.mockClear();
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_1", stringify({"state":"ON"}), {"retain": false, qos: 0}, expect.any(Function));
});
it('Should publish state change for group when members state change', async () => {
// Created for https://github.com/Koenkk/zigbee2mqtt/issues/5725
const device = zigbeeHerdsman.devices.bulb_color;
+81 -11
View File
@@ -104,8 +104,12 @@ describe('HomeAssistant extension', () => {
"breathe",
"okay",
"channel_change",
"candle",
"fireplace",
"colorloop",
"finish_effect",
"stop_effect"
"stop_effect",
"stop_hue_effect",
],
"unique_id":"9_light_zigbee2mqtt"
};
@@ -956,7 +960,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb_color',
stringify({"color":{"hue": 0, "saturation": 100, "h": 0, "s": 100}, "color_mode": "hs", "linkquality": null, "state": null, "update_available": null, "power_on_behavior":null, "update": {"state": null, "installed_version": "unknown"}}),
stringify({"color":{"hue": 0, "saturation": 100, "h": 0, "s": 100}, "color_mode": "hs", "linkquality": null, "state": null, "update_available": null, "power_on_behavior":null, "update": {"state": null, "installed_version": -1, "latest_version": -1}}),
{ retain: false, qos: 0 },
expect.any(Function),
);
@@ -972,7 +976,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb_color',
stringify({"color": {"x": 0.4576,"y": 0.41}, "color_mode": "xy", "linkquality": null,"state": null, "update_available": null, "power_on_behavior":null, "update": {"state": null, "installed_version": "unknown"}}),
stringify({"color": {"x": 0.4576,"y": 0.41}, "color_mode": "xy", "linkquality": null,"state": null, "update_available": null, "power_on_behavior":null, "update": {"state": null, "installed_version": -1, "latest_version": -1}}),
{ retain: false, qos: 0 },
expect.any(Function),
);
@@ -988,7 +992,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb_color',
stringify({"linkquality": null,"state": "ON", "update_available": null, "power_on_behavior": null, "update": {"state": null, "installed_version": "unknown"}}),
stringify({"linkquality": null,"state": "ON", "update_available": null, "power_on_behavior": null, "update": {"state": null, "installed_version": -1, "latest_version": -1}}),
{ retain: false, qos: 0 },
expect.any(Function),
);
@@ -1076,13 +1080,13 @@ describe('HomeAssistant extension', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb',
stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99,"power_on_behavior":null, "update_available": null, "update": {"state": null, "installed_version": "unknown"}}),
stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99,"power_on_behavior":null, "update_available": null, "update": {"state": null, "installed_version": -1, "latest_version": -1}}),
{ retain: true, qos: 0 },
expect.any(Function)
);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/remote',
stringify({"action":null,"action_duration":null,"battery":null,"brightness":255,"linkquality":null, "update_available": null, "update": {"state": null, "installed_version": "unknown"}}),
stringify({"action":null,"action_duration":null,"battery":null,"brightness":255,"linkquality":null, "update_available": null, "update": {"state": null, "installed_version": -1, "latest_version": -1}}),
{ retain: true, qos: 0 },
expect.any(Function)
);
@@ -1106,13 +1110,13 @@ describe('HomeAssistant extension', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb',
stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99,"power_on_behavior":null, "update_available": null, "update": {"state": null, "installed_version": "unknown"}}),
stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99,"power_on_behavior":null, "update_available": null, "update": {"state": null, "installed_version": -1, "latest_version": -1}}),
{ retain: true, qos: 0 },
expect.any(Function)
);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/remote',
stringify({"action":null,"action_duration":null,"battery":null,"brightness":255,"linkquality":null, "update_available": null, "update": {"state": null, "installed_version": "unknown"}}),
stringify({"action":null,"action_duration":null,"battery":null,"brightness":255,"linkquality":null, "update_available": null, "update": {"state": null, "installed_version": -1, "latest_version": -1}}),
{ retain: true, qos: 0 },
expect.any(Function)
);
@@ -1322,8 +1326,12 @@ describe('HomeAssistant extension', () => {
"breathe",
"okay",
"channel_change",
"candle",
"fireplace",
"colorloop",
"finish_effect",
"stop_effect"
"stop_effect",
"stop_hue_effect",
],
"unique_id":"9_light_zigbee2mqtt"
};
@@ -1847,8 +1855,12 @@ describe('HomeAssistant extension', () => {
"breathe",
"okay",
"channel_change",
"candle",
"fireplace",
"colorloop",
"finish_effect",
"stop_effect"
"stop_effect",
"stop_hue_effect",
],
"unique_id":"9_light_zigbee2mqtt"
};
@@ -1892,8 +1904,12 @@ describe('HomeAssistant extension', () => {
"breathe",
"okay",
"channel_change",
"candle",
"fireplace",
"colorloop",
"finish_effect",
"stop_effect"
"stop_effect",
"stop_hue_effect",
],
"unique_id":"9_light_zigbee2mqtt"
};
@@ -1906,6 +1922,60 @@ describe('HomeAssistant extension', () => {
);
});
it('Should discover with availability offline when device is disabled', async () => {
settings.set(['devices', '0x000b57fffec6a5b2', 'disabled'], true);
await resetExtension();
const payload = {
"availability":[
{
"topic":"zigbee2mqtt/bridge/state",
"value_template": `{{ "offline" }}`,
}
],
"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)",
"name":"bulb",
"sw_version":null
},
"effect":true,
"effect_list":[
"blink",
"breathe",
"okay",
"channel_change",
"finish_effect",
"stop_effect"
],
"json_attributes_topic":"zigbee2mqtt/bulb",
"max_mireds":454,
"min_mireds":250,
"name":"bulb",
"schema":"json",
"state_topic":"zigbee2mqtt/bulb",
"supported_color_modes":[
"color_temp"
],
"unique_id":"0x000b57fffec6a5b2_light_zigbee2mqtt"
};
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/light/0x000b57fffec6a5b2/light/config',
stringify(payload),
{ retain: true, qos: 0 },
expect.any(Function),
);
});
it('Should discover last_seen when enabled', async () => {
settings.set(['advanced', 'last_seen'], 'ISO_8601');
await resetExtension();
+17 -2
View File
@@ -102,7 +102,7 @@ describe('Networkmap', () => {
let call = MQTT.publish.mock.calls[0];
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/networkmap/raw');
const expected = {"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), effect, power_on_behavior, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Aqara double key wireless wall switch (2016 model)","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Xiaomi"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":null,"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description": "external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]};
const expected = {"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Aqara double key wireless wall switch (2016 model)","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Xiaomi"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":null,"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description": "external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]};
expect(JSON.parse(call[1])).toStrictEqual(expected);
/**
@@ -276,7 +276,7 @@ describe('Networkmap', () => {
let call = MQTT.publish.mock.calls[0];
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
const expected = {"data":{"routes":true,"type":"raw","value":{"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), effect, power_on_behavior, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Aqara double key wireless wall switch (2016 model)","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Xiaomi"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":null,"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]}},"status":"ok"};
const expected = {"data":{"routes":true,"type":"raw","value":{"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Aqara double key wireless wall switch (2016 model)","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Xiaomi"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":null,"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]}},"status":"ok"};
const actual = JSON.parse(call[1]);
expect(actual).toStrictEqual(expected);
});
@@ -292,4 +292,19 @@ describe('Networkmap', () => {
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Should exclude disabled devices from networkmap', async () => {
settings.set(['devices', '0x000b57fffec6a5b2', 'disabled'], true);
mock();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/networkmap', stringify({type: 'raw', routes: true}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
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":"Aqara double key wireless wall switch (2016 model)","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Xiaomi"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":null,"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]}},"status":"ok"};
const actual = JSON.parse(call[1]);
expect(actual).toStrictEqual(expected);
});
});
+17 -14
View File
@@ -36,6 +36,7 @@ describe('OTA update', () => {
jest.useFakeTimers();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await jest.runOnlyPendingTimers();
await flushPromises();
});
@@ -71,6 +72,7 @@ describe('OTA update', () => {
mapped.ota.updateToLatest.mockImplementationOnce((a, b, onUpdate) => {
onUpdate(0, null);
onUpdate(10, 3600.2123);
return 90;
});
MQTT.events.message('zigbee2mqtt/bridge/request/device/ota_update/update', 'bulb');
@@ -98,7 +100,7 @@ describe('OTA update', () => {
);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb',
stringify({"update_available":false,"update":{"state":"idle"}}),
stringify({"update_available":false,"update":{"state":"idle","installed_version":90,"latest_version":90}}),
{retain: true, qos: 0}, expect.any(Function)
);
expect(MQTT.publish).toHaveBeenCalledWith(
@@ -144,7 +146,7 @@ describe('OTA update', () => {
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
mockClear(mapped);
mapped.ota.isUpdateAvailable.mockReturnValueOnce(false);
mapped.ota.isUpdateAvailable.mockReturnValueOnce({available: false, currentFileVersion: 10, otaFileVersion: 10});
MQTT.events.message('zigbee2mqtt/bridge/request/device/ota_update/check', "bulb");
await flushPromises();
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(1);
@@ -156,7 +158,7 @@ describe('OTA update', () => {
);
MQTT.publish.mockClear();
mapped.ota.isUpdateAvailable.mockReturnValueOnce(true);
mapped.ota.isUpdateAvailable.mockReturnValueOnce({available: true, currentFileVersion: 10, otaFileVersion: 12});
MQTT.events.message('zigbee2mqtt/bridge/request/device/ota_update/check', "bulb");
await flushPromises();
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(2);
@@ -249,7 +251,7 @@ describe('OTA update', () => {
const data = {imageType: 12382};
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
mockClear(mapped);
mapped.ota.isUpdateAvailable.mockReturnValueOnce(true);
mapped.ota.isUpdateAvailable.mockReturnValueOnce({available: true, currentFileVersion: 10, otaFileVersion: 12});
const payload = {data, cluster: 'genOta', device, endpoint: device.getEndpoint(1), type: 'commandQueryNextImageRequest', linkquality: 10};
logger.info.mockClear();
await zigbeeHerdsman.events.message(payload);
@@ -266,13 +268,13 @@ describe('OTA update', () => {
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(1);
logger.info.mockClear();
mapped.ota.isUpdateAvailable.mockReturnValueOnce(false);
mapped.ota.isUpdateAvailable.mockReturnValueOnce({available: false, currentFileVersion: 10, otaFileVersion: 10});
await zigbeeHerdsman.events.message(payload);
await flushPromises();
expect(logger.info).not.toHaveBeenCalledWith(`Update available for 'bulb'`);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb',
stringify({"update_available":true,"update":{"state":"available"}}),
stringify({"update_available":true,"update":{"state":"available","installed_version":10,"latest_version":12}}),
{retain: true, qos: 0}, expect.any(Function)
);
});
@@ -305,7 +307,7 @@ describe('OTA update', () => {
const data = {imageType: 12382};
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
mockClear(mapped);
mapped.ota.isUpdateAvailable.mockReturnValueOnce(false);
mapped.ota.isUpdateAvailable.mockReturnValueOnce({available: false, currentFileVersion: 13, otaFileVersion: 13});
const payload = {data, cluster: 'genOta', device, endpoint: device.getEndpoint(1), type: 'commandQueryNextImageRequest', linkquality: 10};
logger.info.mockClear();
await zigbeeHerdsman.events.message(payload);
@@ -316,7 +318,7 @@ describe('OTA update', () => {
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 0x98});
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb',
stringify({"update_available":false,"update":{"state":"idle"}}),
stringify({"update_available":false,"update":{"state":"idle","installed_version": 13, "latest_version": 13}}),
{retain: true, qos: 0}, expect.any(Function)
);
});
@@ -327,7 +329,7 @@ describe('OTA update', () => {
const data = {imageType: 12382};
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
mockClear(mapped);
mapped.ota.isUpdateAvailable.mockReturnValueOnce(true);
mapped.ota.isUpdateAvailable.mockReturnValueOnce({available: true, currentFileVersion: 10, otaFileVersion: 13});
const payload = {data, cluster: 'genOta', device, endpoint: device.getEndpoint(1), type: 'commandQueryNextImageRequest', linkquality: 10};
logger.info.mockClear();
await zigbeeHerdsman.events.message(payload);
@@ -345,15 +347,15 @@ describe('OTA update', () => {
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 152});
});
it('Shouldnt respond with NO_IMAGE_AVAILABLE when not supporting OTA and device has no OTA endpoint', async () => {
it('Should respond with NO_IMAGE_AVAILABLE when not supporting OTA and device has no OTA endpoint to standard endpoint', async () => {
const device = zigbeeHerdsman.devices.SV01;
const data = {imageType: 12382};
const payload = {data, cluster: 'genOta', device, endpoint: device.getEndpoint(1), type: 'commandQueryNextImageRequest', linkquality: 10};
logger.error.mockClear();
await zigbeeHerdsman.events.message(payload);
await flushPromises();
expect(device.endpoints[0].commandResponse).toHaveBeenCalledTimes(0);
expect(logger.error).toHaveBeenCalledTimes(0);
expect(device.endpoints[0].commandResponse).toHaveBeenCalledTimes(1);
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 152});
});
it('Legacy api: Should OTA update a device', async () => {
@@ -372,6 +374,7 @@ describe('OTA update', () => {
mapped.ota.updateToLatest.mockImplementationOnce((a, b, onUpdate) => {
onUpdate(0, null);
onUpdate(10, 3600);
return 91;
});
MQTT.events.message('zigbee2mqtt/bridge/ota_update/update', 'bulb');
@@ -414,7 +417,7 @@ describe('OTA update', () => {
mockClear(mapped);
logger.info.mockClear();
mapped.ota.isUpdateAvailable.mockReturnValueOnce(false);
mapped.ota.isUpdateAvailable.mockReturnValueOnce({available: false, currentFileVersion: 13, otaFileVersion: 13});
MQTT.events.message('zigbee2mqtt/bridge/ota_update/check', 'bulb');
await flushPromises();
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(1);
@@ -422,7 +425,7 @@ describe('OTA update', () => {
expect(logger.info).toHaveBeenCalledWith(`No update available for 'bulb'`);
logger.info.mockClear();
mapped.ota.isUpdateAvailable.mockReturnValueOnce(true);
mapped.ota.isUpdateAvailable.mockReturnValueOnce({available: true, currentFileVersion: 13, otaFileVersion: 15});
MQTT.events.message('zigbee2mqtt/bridge/ota_update/check', 'bulb');
await flushPromises();
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(2);
+2
View File
@@ -16,6 +16,8 @@ describe('Receive', () => {
jest.useFakeTimers();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await jest.runOnlyPendingTimers();
await flushPromises();
});
beforeEach(async () => {
+23 -7
View File
@@ -123,6 +123,22 @@ describe('Settings', () => {
expect(actual).toStrictEqual(expected);
});
it('Should add devices even when devices exist empty', () => {
write(configurationFile, {devices: []});
settings.addDevice('0x12345678');
const actual = read(configurationFile);
const expected = {
devices: {
'0x12345678': {
friendly_name: '0x12345678',
},
},
};
expect(actual).toStrictEqual(expected);
});
it('Should read devices', () => {
const content = {
devices: {
@@ -904,7 +920,7 @@ describe('Settings', () => {
it('Frontend config', () => {
write(configurationFile, {...minimalConfig,
frontend: true,
frontend: true,
});
settings.reRead();
@@ -913,7 +929,7 @@ describe('Settings', () => {
it('Baudrate config', () => {
write(configurationFile, {...minimalConfig,
advanced: {baudrate: 20},
advanced: {baudrate: 20},
});
settings.reRead();
@@ -922,7 +938,7 @@ describe('Settings', () => {
it('ikea_ota_use_test_url config', () => {
write(configurationFile, {...minimalConfig,
advanced: {ikea_ota_use_test_url: true},
advanced: {ikea_ota_use_test_url: true},
});
settings.reRead();
@@ -931,7 +947,7 @@ describe('Settings', () => {
it('transmit_power config', () => {
write(configurationFile, {...minimalConfig,
experimental: {transmit_power: 1337},
experimental: {transmit_power: 1337},
});
settings.reRead();
@@ -940,7 +956,7 @@ describe('Settings', () => {
it('output config', () => {
write(configurationFile, {...minimalConfig,
experimental: {output: 'json'},
experimental: {output: 'json'},
});
settings.reRead();
@@ -949,7 +965,7 @@ describe('Settings', () => {
it('Baudrartsctste config', () => {
write(configurationFile, {...minimalConfig,
advanced: {rtscts: true},
advanced: {rtscts: true},
});
settings.reRead();
@@ -958,7 +974,7 @@ describe('Settings', () => {
it('Deprecated: Home Assistant config', () => {
write(configurationFile, {...minimalConfig,
homeassistant: {discovery_topic: 'new'},
homeassistant: {discovery_topic: 'new'},
advanced: {homeassistant_discovery_topic: 'old', homeassistant_status_topic: 'olds'},
});