zigbee-herdsman-converters 25.9.0 → 25.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/dist/devices/bosch.d.ts.map +1 -1
- package/dist/devices/bosch.js +379 -54
- package/dist/devices/bosch.js.map +1 -1
- package/dist/devices/custom_devices_diy.d.ts.map +1 -1
- package/dist/devices/custom_devices_diy.js +11 -3
- package/dist/devices/custom_devices_diy.js.map +1 -1
- package/dist/devices/giex.js +2 -2
- package/dist/devices/innr.d.ts.map +1 -1
- package/dist/devices/innr.js +7 -0
- package/dist/devices/innr.js.map +1 -1
- package/dist/devices/inovelli.d.ts.map +1 -1
- package/dist/devices/inovelli.js +201 -223
- package/dist/devices/inovelli.js.map +1 -1
- package/dist/devices/lumi.d.ts.map +1 -1
- package/dist/devices/lumi.js +7 -2
- package/dist/devices/lumi.js.map +1 -1
- package/dist/devices/onokom.d.ts.map +1 -1
- package/dist/devices/onokom.js +621 -51
- package/dist/devices/onokom.js.map +1 -1
- package/dist/devices/philips.d.ts.map +1 -1
- package/dist/devices/philips.js +17 -2
- package/dist/devices/philips.js.map +1 -1
- package/dist/devices/seastar_intelligence.js +1 -1
- package/dist/devices/seastar_intelligence.js.map +1 -1
- package/dist/devices/slacky_diy.d.ts.map +1 -1
- package/dist/devices/slacky_diy.js +249 -2
- package/dist/devices/slacky_diy.js.map +1 -1
- package/dist/devices/sprut.d.ts.map +1 -1
- package/dist/devices/sprut.js +124 -0
- package/dist/devices/sprut.js.map +1 -1
- package/dist/devices/tuya.d.ts.map +1 -1
- package/dist/devices/tuya.js +12 -6
- package/dist/devices/tuya.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/models-index.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [25.11.0](https://github.com/Koenkk/zigbee-herdsman-converters/compare/v25.10.0...v25.11.0) (2025-08-28)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **add:** AE 287 C ([#9864](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9864)) ([5f927fd](https://github.com/Koenkk/zigbee-herdsman-converters/commit/5f927fd292fbba0953c5a637002d7ffe952489bb))
|
|
9
|
+
* **add:** Drivent ([#9874](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9874)) ([c8dcb6f](https://github.com/Koenkk/zigbee-herdsman-converters/commit/c8dcb6f2bedef654698bd2840beaaf79005d596d))
|
|
10
|
+
* **add:** m5NanoC6 ([#9866](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9866)) ([95ca5db](https://github.com/Koenkk/zigbee-herdsman-converters/commit/95ca5db499581c2687ec830e1f98fa5a2ae3e055))
|
|
11
|
+
* Bosch `BSP-FZ2` (Plug compact II): Support reset of energy meter and lower min value change on electricityMeter reporting ([#9865](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9865)) ([0615f2d](https://github.com/Koenkk/zigbee-herdsman-converters/commit/0615f2d6999ab011ef22b7ee9414ae1181b6df24))
|
|
12
|
+
* Onokom: various improvements ([#9869](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9869)) ([15acfc4](https://github.com/Koenkk/zigbee-herdsman-converters/commit/15acfc4f8e148e5b09546541608e2c1822ee8ec4))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* Create inovelliMMWave modern extend to relocate mmWave commands and support future expansion ([#9870](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9870)) ([a88b08c](https://github.com/Koenkk/zigbee-herdsman-converters/commit/a88b08ca0093d38d9bffeeecd9ad55b5cfbc793f))
|
|
18
|
+
* **detect:** Detect `_TZ3000_fawk5xjv` and `_TZ3000_ok0ggpk7` as a Detect _TZ3000_fawk5xjv and _TZ3000_ok0ggpk7 as a TS0003_switch_3_gang_with_backlight ([#9861](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9861)) ([1b539d2](https://github.com/Koenkk/zigbee-herdsman-converters/commit/1b539d2a9309432fcaf34236feddc523e56027a0))
|
|
19
|
+
* **detect:** Detect `_TZE200_jfw0a4aa` as ZG-102ZM and `_TZE200_hdih4foa` as ZG-204ZH ([#9872](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9872)) ([8437961](https://github.com/Koenkk/zigbee-herdsman-converters/commit/8437961b762ce769cd10fadd0dd2a731aac2a5ed))
|
|
20
|
+
* **detect:** Detect `_TZE284_1lvln0x6` as iHsenso_TS0601_human_presence https://github.com/Koenkk/zigbee2mqtt/issues/28398 ([b60f84f](https://github.com/Koenkk/zigbee-herdsman-converters/commit/b60f84fd14036c12a380f6f04e2b91681c2d5ec0))
|
|
21
|
+
* **detect:** Detect `lumi.light.agl002` as Aqara T2_E26 https://github.com/Koenkk/zigbee2mqtt/issues/26182 ([e221d02](https://github.com/Koenkk/zigbee-herdsman-converters/commit/e221d023ec8d188c4f25ba3b19bdd87c0154b3a0))
|
|
22
|
+
* **ignore:** Bosch `BMCT-SLZ` (Light/shutter control unit II): Only expose attributes compatible with the selected switch type ([#9862](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9862)) ([7eecc4e](https://github.com/Koenkk/zigbee-herdsman-converters/commit/7eecc4e751812a27ce65f38565199dcd63cac809))
|
|
23
|
+
|
|
24
|
+
## [25.10.0](https://github.com/Koenkk/zigbee-herdsman-converters/compare/v25.9.0...v25.10.0) (2025-08-27)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Features
|
|
28
|
+
|
|
29
|
+
* **add:** 929003808801 ([#9853](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9853)) ([e1e7f12](https://github.com/Koenkk/zigbee-herdsman-converters/commit/e1e7f12bf5210a9a363ee46451908f6c0f5207b7))
|
|
30
|
+
* **add:** 929004297402 https://github.com/Koenkk/zigbee2mqtt/issues/28397 ([d238f93](https://github.com/Koenkk/zigbee-herdsman-converters/commit/d238f93bb8934fc51fe43a895d0b6adc09c1423c))
|
|
31
|
+
* **add:** AirQ_Monitor_S01 ([#9841](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9841)) ([ff8b222](https://github.com/Koenkk/zigbee-herdsman-converters/commit/ff8b222636a76ee0ce98e1cc11bbcfeb601934d9))
|
|
32
|
+
* **add:** FUT068ZR ([#9859](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9859)) ([56105e0](https://github.com/Koenkk/zigbee-herdsman-converters/commit/56105e0016f4e9e695998c91ba2161f8c2cbbf73))
|
|
33
|
+
* Zemismart TB26-3: support more features ([#9849](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9849)) ([067a499](https://github.com/Koenkk/zigbee-herdsman-converters/commit/067a499849b2630fea779cbfa4584ccb55816a70))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Bug Fixes
|
|
37
|
+
|
|
38
|
+
* Bosch `BMCT-SLZ`: various enhancements and fixes ([#9852](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9852)) ([2902e7c](https://github.com/Koenkk/zigbee-herdsman-converters/commit/2902e7ca4d9f422f35375501652e3ea79e854d74))
|
|
39
|
+
* Correct vendor from GiEX to GIEX ([#9848](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9848)) ([d214855](https://github.com/Koenkk/zigbee-herdsman-converters/commit/d21485546be6642d3a929c11f0b49bd17473bcb3))
|
|
40
|
+
* Create custom modernExtend classes to use with Inovelli devices ([#9835](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9835)) ([e3e76ad](https://github.com/Koenkk/zigbee-herdsman-converters/commit/e3e76ad2b628a4841b248ad163484c8dd8455354))
|
|
41
|
+
* **detect:** Detect `929003811201` as Philips 5633030P6 ([#9851](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9851)) ([ba349f0](https://github.com/Koenkk/zigbee-herdsman-converters/commit/ba349f0728767df85cbee1884a51f6dc6f250eb5))
|
|
42
|
+
* Tuya `_TZ3000_okaz9tjs`: fix configure failing https://github.com/Koenkk/zigbee-herdsman-converters/issues/9786 ([1d9f8bb](https://github.com/Koenkk/zigbee-herdsman-converters/commit/1d9f8bbc1309d91e71368b9ff71db19b50246bda))
|
|
43
|
+
* Various code fixes ([#9854](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9854)) ([7ba943e](https://github.com/Koenkk/zigbee-herdsman-converters/commit/7ba943e6abaa9978c00d048dfeac80d3721fd4ae))
|
|
44
|
+
|
|
3
45
|
## [25.9.0](https://github.com/Koenkk/zigbee-herdsman-converters/compare/v25.8.0...v25.9.0) (2025-08-25)
|
|
4
46
|
|
|
5
47
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bosch.d.ts","sourceRoot":"","sources":["../../src/devices/bosch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"bosch.d.ts","sourceRoot":"","sources":["../../src/devices/bosch.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAC,oBAAoB,EAA6C,MAAM,cAAc,CAAC;AA44CnG,eAAO,MAAM,WAAW,EAAE,oBAAoB,EAmsC7C,CAAC"}
|
package/dist/devices/bosch.js
CHANGED
|
@@ -38,10 +38,12 @@ const zigbee_herdsman_1 = require("zigbee-herdsman");
|
|
|
38
38
|
const fz = __importStar(require("../converters/fromZigbee"));
|
|
39
39
|
const tz = __importStar(require("../converters/toZigbee"));
|
|
40
40
|
const constants = __importStar(require("../lib/constants"));
|
|
41
|
+
const constants_1 = require("../lib/constants");
|
|
41
42
|
const exposes = __importStar(require("../lib/exposes"));
|
|
42
43
|
const logger_1 = require("../lib/logger");
|
|
43
44
|
const m = __importStar(require("../lib/modernExtend"));
|
|
44
45
|
const reporting = __importStar(require("../lib/reporting"));
|
|
46
|
+
const reporting_1 = require("../lib/reporting");
|
|
45
47
|
const globalStore = __importStar(require("../lib/store"));
|
|
46
48
|
const utils = __importStar(require("../lib/utils"));
|
|
47
49
|
const e = exposes.presets;
|
|
@@ -352,6 +354,38 @@ const boschExtend = {
|
|
|
352
354
|
isModernExtend: true,
|
|
353
355
|
};
|
|
354
356
|
},
|
|
357
|
+
seMeteringCluster: () => m.deviceAddCustomCluster("seMetering", {
|
|
358
|
+
ID: zigbee_herdsman_1.Zcl.Clusters.seMetering.ID,
|
|
359
|
+
attributes: {},
|
|
360
|
+
commands: {
|
|
361
|
+
resetEnergyReading: {
|
|
362
|
+
ID: 0x80,
|
|
363
|
+
parameters: [],
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
commandsResponse: {},
|
|
367
|
+
}),
|
|
368
|
+
resetEnergyReading: () => {
|
|
369
|
+
const exposes = [
|
|
370
|
+
e
|
|
371
|
+
.enum("reset_energy_reading", ea.SET, ["reset"])
|
|
372
|
+
.withDescription("Triggers the reset of the energy reading to 0 kWh.")
|
|
373
|
+
.withCategory("config"),
|
|
374
|
+
];
|
|
375
|
+
const toZigbee = [
|
|
376
|
+
{
|
|
377
|
+
key: ["reset_energy_reading"],
|
|
378
|
+
convertSet: async (entity, key, value, meta) => {
|
|
379
|
+
await entity.command("seMetering", "resetEnergyReading", {}, manufacturerOptions);
|
|
380
|
+
},
|
|
381
|
+
},
|
|
382
|
+
];
|
|
383
|
+
return {
|
|
384
|
+
exposes,
|
|
385
|
+
toZigbee,
|
|
386
|
+
isModernExtend: true,
|
|
387
|
+
};
|
|
388
|
+
},
|
|
355
389
|
doorWindowContact: (hasVibrationSensor) => {
|
|
356
390
|
const exposes = [
|
|
357
391
|
e.binary("contact", ea.STATE, false, true).withDescription("Indicates whether the device is opened or closed"),
|
|
@@ -789,21 +823,59 @@ const boschExtend = {
|
|
|
789
823
|
stopped: 0x00,
|
|
790
824
|
opening: 0x01,
|
|
791
825
|
closing: 0x02,
|
|
826
|
+
unknownOne: 0x03,
|
|
827
|
+
unknownTwo: 0x04,
|
|
792
828
|
};
|
|
793
829
|
const stateSwitchType = {
|
|
794
830
|
button: 0x01,
|
|
795
831
|
button_key_change: 0x02,
|
|
796
832
|
rocker_switch: 0x03,
|
|
797
833
|
rocker_switch_key_change: 0x04,
|
|
834
|
+
none: 0x00,
|
|
835
|
+
};
|
|
836
|
+
const stateSwitchMode = {
|
|
837
|
+
coupled: 0x00,
|
|
838
|
+
decoupled: 0x01,
|
|
839
|
+
only_short_press_decoupled: 0x02,
|
|
840
|
+
only_long_press_decoupled: 0x03,
|
|
798
841
|
};
|
|
799
842
|
const stateOffOn = {
|
|
800
843
|
OFF: 0x00,
|
|
801
844
|
ON: 0x01,
|
|
802
845
|
};
|
|
803
846
|
const fromZigbee = [
|
|
804
|
-
fz.
|
|
847
|
+
fz.on_off_force_multiendpoint,
|
|
805
848
|
fz.power_on_behavior,
|
|
806
849
|
fz.cover_position_tilt,
|
|
850
|
+
{
|
|
851
|
+
cluster: "boschSpecific",
|
|
852
|
+
type: ["raw"],
|
|
853
|
+
convert: (model, msg, publish, options, meta) => {
|
|
854
|
+
const command = msg.data[4];
|
|
855
|
+
if (command !== 0x03 && command !== 0x04) {
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
let state;
|
|
859
|
+
const status = msg.data[5];
|
|
860
|
+
const duration = msg.data[6] / 10;
|
|
861
|
+
switch (status) {
|
|
862
|
+
case 0:
|
|
863
|
+
state = "press_released";
|
|
864
|
+
break;
|
|
865
|
+
case 1:
|
|
866
|
+
state = duration !== 0 ? "hold" : "hold_released";
|
|
867
|
+
break;
|
|
868
|
+
case 2:
|
|
869
|
+
state = "closed";
|
|
870
|
+
break;
|
|
871
|
+
case 3:
|
|
872
|
+
state = "opened";
|
|
873
|
+
break;
|
|
874
|
+
}
|
|
875
|
+
const triggeredSide = command === 0x03 ? "left" : "right";
|
|
876
|
+
return { action: `${state}_${triggeredSide}`, action_duration: duration };
|
|
877
|
+
},
|
|
878
|
+
},
|
|
807
879
|
{
|
|
808
880
|
cluster: "boschSpecific",
|
|
809
881
|
type: ["attributeReport", "readResponse"],
|
|
@@ -819,7 +891,16 @@ const boschExtend = {
|
|
|
819
891
|
}
|
|
820
892
|
}
|
|
821
893
|
if (data.switchType !== undefined) {
|
|
822
|
-
|
|
894
|
+
const switchType = msg.data.switchType;
|
|
895
|
+
result.switch_type = Object.keys(stateSwitchType).find((key) => stateSwitchType[key] === switchType);
|
|
896
|
+
if (switchType !== meta.device.meta.switchType) {
|
|
897
|
+
meta.device.meta.switchType = switchType;
|
|
898
|
+
meta.deviceExposesChanged();
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
if (data.switchMode !== undefined) {
|
|
902
|
+
const property = utils.postfixWithEndpointName("switch_mode", msg, model, meta);
|
|
903
|
+
result[property] = Object.keys(stateSwitchMode).find((key) => stateSwitchMode[key] === msg.data.switchMode);
|
|
823
904
|
}
|
|
824
905
|
if (data.calibrationOpeningTime !== undefined) {
|
|
825
906
|
result.calibration_opening_time = msg.data.calibrationOpeningTime / 10;
|
|
@@ -840,6 +921,14 @@ const boschExtend = {
|
|
|
840
921
|
if (data.motorState !== undefined) {
|
|
841
922
|
result.motor_state = Object.keys(stateMotor).find((key) => stateMotor[key] === msg.data.motorState);
|
|
842
923
|
}
|
|
924
|
+
if (data.autoOffEnabled !== undefined) {
|
|
925
|
+
const property = utils.postfixWithEndpointName("auto_off_enabled", msg, model, meta);
|
|
926
|
+
result[property] = msg.data.autoOffEnabled === 1 ? "ON" : "OFF";
|
|
927
|
+
}
|
|
928
|
+
if (data.autoOffTime !== undefined) {
|
|
929
|
+
const property = utils.postfixWithEndpointName("auto_off_time", msg, model, meta);
|
|
930
|
+
result[property] = msg.data.autoOffTime / 60;
|
|
931
|
+
}
|
|
843
932
|
return result;
|
|
844
933
|
},
|
|
845
934
|
},
|
|
@@ -848,7 +937,17 @@ const boschExtend = {
|
|
|
848
937
|
tz.power_on_behavior,
|
|
849
938
|
tz.cover_position_tilt,
|
|
850
939
|
{
|
|
851
|
-
key: [
|
|
940
|
+
key: [
|
|
941
|
+
"device_mode",
|
|
942
|
+
"switch_type",
|
|
943
|
+
"switch_mode",
|
|
944
|
+
"child_lock",
|
|
945
|
+
"state",
|
|
946
|
+
"on_time",
|
|
947
|
+
"off_wait_time",
|
|
948
|
+
"auto_off_enabled",
|
|
949
|
+
"auto_off_time",
|
|
950
|
+
],
|
|
852
951
|
convertSet: async (entity, key, value, meta) => {
|
|
853
952
|
if (key === "state") {
|
|
854
953
|
if ("ID" in entity && entity.ID === 1) {
|
|
@@ -870,15 +969,44 @@ const boschExtend = {
|
|
|
870
969
|
return { state: { device_mode: value } };
|
|
871
970
|
}
|
|
872
971
|
if (key === "switch_type") {
|
|
873
|
-
const
|
|
874
|
-
|
|
972
|
+
const applyDefaultForSwitchModeAndChildLock = async (endpoint) => {
|
|
973
|
+
const switchModeDefault = utils.getFromLookup("coupled", stateSwitchMode);
|
|
974
|
+
const childLockDefault = utils.getFromLookup("OFF", stateOffOn);
|
|
975
|
+
await endpoint.write("boschSpecific", {
|
|
976
|
+
switchMode: switchModeDefault,
|
|
977
|
+
childLock: childLockDefault,
|
|
978
|
+
});
|
|
979
|
+
await endpoint.read("boschSpecific", ["switchMode", "childLock"]);
|
|
980
|
+
};
|
|
981
|
+
const switchType = utils.getFromLookup(value, stateSwitchType);
|
|
982
|
+
await entity.write("boschSpecific", { switchType: switchType });
|
|
983
|
+
await entity.read("boschSpecific", ["switchType"]);
|
|
984
|
+
await applyDefaultForSwitchModeAndChildLock(entity);
|
|
985
|
+
const leftEndpoint = meta.device.getEndpoint(2);
|
|
986
|
+
await applyDefaultForSwitchModeAndChildLock(leftEndpoint);
|
|
987
|
+
const rightEndpoint = meta.device.getEndpoint(3);
|
|
988
|
+
await applyDefaultForSwitchModeAndChildLock(rightEndpoint);
|
|
875
989
|
return { state: { switch_type: value } };
|
|
876
990
|
}
|
|
991
|
+
if (key === "switch_mode") {
|
|
992
|
+
const index = utils.getFromLookup(value, stateSwitchMode);
|
|
993
|
+
await entity.write("boschSpecific", { switchMode: index });
|
|
994
|
+
return { state: { switch_mode: value } };
|
|
995
|
+
}
|
|
877
996
|
if (key === "child_lock") {
|
|
878
997
|
const index = utils.getFromLookup(value, stateOffOn);
|
|
879
998
|
await entity.write("boschSpecific", { childLock: index });
|
|
880
999
|
return { state: { child_lock: value } };
|
|
881
1000
|
}
|
|
1001
|
+
if (key === "auto_off_enabled") {
|
|
1002
|
+
const index = utils.getFromLookup(value, stateOffOn);
|
|
1003
|
+
await entity.write("boschSpecific", { autoOffEnabled: index });
|
|
1004
|
+
return { state: { auto_off_enabled: value } };
|
|
1005
|
+
}
|
|
1006
|
+
if (key === "auto_off_time" && typeof value === "number") {
|
|
1007
|
+
await entity.write("boschSpecific", { autoOffTime: value * 60 });
|
|
1008
|
+
return { state: { auto_off_time: value } };
|
|
1009
|
+
}
|
|
882
1010
|
},
|
|
883
1011
|
convertGet: async (entity, key, meta) => {
|
|
884
1012
|
switch (key) {
|
|
@@ -895,9 +1023,18 @@ const boschExtend = {
|
|
|
895
1023
|
case "switch_type":
|
|
896
1024
|
await entity.read("boschSpecific", ["switchType"]);
|
|
897
1025
|
break;
|
|
1026
|
+
case "switch_mode":
|
|
1027
|
+
await entity.read("boschSpecific", ["switchMode"]);
|
|
1028
|
+
break;
|
|
898
1029
|
case "child_lock":
|
|
899
1030
|
await entity.read("boschSpecific", ["childLock"]);
|
|
900
1031
|
break;
|
|
1032
|
+
case "auto_off_enabled":
|
|
1033
|
+
await entity.read("boschSpecific", ["autoOffEnabled"]);
|
|
1034
|
+
break;
|
|
1035
|
+
case "auto_off_time":
|
|
1036
|
+
await entity.read("boschSpecific", ["autoOffTime"]);
|
|
1037
|
+
break;
|
|
901
1038
|
default:
|
|
902
1039
|
throw new Error(`Unhandled key boschExtend.bmct.toZigbee.convertGet ${key}`);
|
|
903
1040
|
}
|
|
@@ -1761,7 +1898,17 @@ exports.definitions = [
|
|
|
1761
1898
|
model: "BSP-FZ2",
|
|
1762
1899
|
vendor: "Bosch",
|
|
1763
1900
|
description: "Plug compact EU",
|
|
1764
|
-
extend: [
|
|
1901
|
+
extend: [
|
|
1902
|
+
m.onOff(),
|
|
1903
|
+
m.electricityMeter({
|
|
1904
|
+
voltage: false,
|
|
1905
|
+
current: false,
|
|
1906
|
+
power: { change: 1 },
|
|
1907
|
+
energy: { change: 1 },
|
|
1908
|
+
}),
|
|
1909
|
+
boschExtend.seMeteringCluster(),
|
|
1910
|
+
boschExtend.resetEnergyReading(),
|
|
1911
|
+
],
|
|
1765
1912
|
ota: true,
|
|
1766
1913
|
whiteLabel: [
|
|
1767
1914
|
{ vendor: "Bosch", model: "BSP-EZ2", description: "Plug compact FR", fingerprint: [{ modelID: "RBSH-SP-ZB-FR" }] },
|
|
@@ -1834,48 +1981,130 @@ exports.definitions = [
|
|
|
1834
1981
|
description: "Light/shutter control unit II",
|
|
1835
1982
|
extend: [
|
|
1836
1983
|
m.deviceEndpoints({ endpoints: { left: 2, right: 3 } }),
|
|
1837
|
-
m.electricityMeter({
|
|
1984
|
+
m.electricityMeter({
|
|
1985
|
+
voltage: false,
|
|
1986
|
+
current: false,
|
|
1987
|
+
power: { change: 1 },
|
|
1988
|
+
energy: { change: 1 },
|
|
1989
|
+
}),
|
|
1838
1990
|
m.deviceAddCustomCluster("boschSpecific", {
|
|
1839
1991
|
ID: 0xfca0,
|
|
1840
1992
|
manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
|
|
1841
1993
|
attributes: {
|
|
1842
1994
|
deviceMode: { ID: 0x0000, type: zigbee_herdsman_1.Zcl.DataType.ENUM8 },
|
|
1843
1995
|
switchType: { ID: 0x0001, type: zigbee_herdsman_1.Zcl.DataType.ENUM8 },
|
|
1996
|
+
switchMode: { ID: 0x0031, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
|
|
1844
1997
|
calibrationOpeningTime: { ID: 0x0002, type: zigbee_herdsman_1.Zcl.DataType.UINT32 },
|
|
1845
1998
|
calibrationClosingTime: { ID: 0x0003, type: zigbee_herdsman_1.Zcl.DataType.UINT32 },
|
|
1999
|
+
// 0x0005 isn't used at all when using the Bosch SHC as of 30-06-2025.
|
|
2000
|
+
// As I don't have any shutters, I can't run all calibration steps
|
|
2001
|
+
// successfully. So, keep any comments regarding these
|
|
2002
|
+
// attributes with caution.
|
|
1846
2003
|
calibrationButtonHoldTime: { ID: 0x0005, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
|
|
2004
|
+
autoOffEnabled: { ID: 0x0006, type: zigbee_herdsman_1.Zcl.DataType.BOOLEAN },
|
|
2005
|
+
autoOffTime: { ID: 0x0007, type: zigbee_herdsman_1.Zcl.DataType.UINT16 },
|
|
1847
2006
|
childLock: { ID: 0x0008, type: zigbee_herdsman_1.Zcl.DataType.BOOLEAN },
|
|
2007
|
+
// 0x000f is only being set when using the automatic calibration.
|
|
2008
|
+
// It's being set to 0 then before sending the calibration
|
|
2009
|
+
// command. Additionally, when changing
|
|
2010
|
+
// the calibrationOpeningTime or calibrationClosingTime in the
|
|
2011
|
+
// Bosch app, it's also being set to 0.
|
|
2012
|
+
// I couldn't find any way to set 0x000f manually in the Bosch app.
|
|
1848
2013
|
calibrationMotorStartDelay: { ID: 0x000f, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
|
|
2014
|
+
calibrationMotorReverseDirection: { ID: 0x0032, type: zigbee_herdsman_1.Zcl.DataType.BOOLEAN },
|
|
1849
2015
|
motorState: { ID: 0x0013, type: zigbee_herdsman_1.Zcl.DataType.ENUM8 },
|
|
2016
|
+
// unknownAttributeOne is always being configured as reporting
|
|
2017
|
+
// attribute on endpoint 1 when using the Bosch SHC.
|
|
2018
|
+
// Can't tell what this attribute does (always received
|
|
2019
|
+
// 0x00 as answer on manual lookup).
|
|
2020
|
+
unknownAttributeOne: { ID: 0x0004, type: zigbee_herdsman_1.Zcl.DataType.BITMAP8 },
|
|
2021
|
+
// Attribute is being set to 255 when deactivating the automatic
|
|
2022
|
+
// detection of the motor end position by the Bosch SHC. After
|
|
2023
|
+
// activating the automatic end position detection it's being set
|
|
2024
|
+
// to 0 by the Bosch SHC. Apart from that, there's no way to manually
|
|
2025
|
+
// change the value.
|
|
2026
|
+
calibrationMotorEndPosition: { ID: 0x0021, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
|
|
2027
|
+
// 0x0033 is used when setting the motor start delay manually
|
|
2028
|
+
// using the Bosch SHC as of 30-06-2025.
|
|
2029
|
+
// If the user wants to automatically detect the delay during
|
|
2030
|
+
// calibration, it's being set to 0 over the Bosch app.
|
|
2031
|
+
calibrationNewMotorStartDelay: { ID: 0x0033, type: zigbee_herdsman_1.Zcl.DataType.UINT16 },
|
|
2032
|
+
// 0x0010 and 0x0011 is being set simultaneously with the same value
|
|
2033
|
+
// when changing the delay for the rotation of the slats on venetian
|
|
2034
|
+
// blinds. Maybe one attribute for each direction?
|
|
2035
|
+
// It's also being configured as reporting attribute when using
|
|
2036
|
+
// venetian blinds.
|
|
2037
|
+
slatRotationDurationOne: { ID: 0x0010, type: zigbee_herdsman_1.Zcl.DataType.UINT32 },
|
|
2038
|
+
slatRotationDurationTwo: { ID: 0x0011, type: zigbee_herdsman_1.Zcl.DataType.UINT32 },
|
|
2039
|
+
// 0x002a is only being used when doing an automatic calibration
|
|
2040
|
+
// with the Bosch specific startAutomaticMotorCalibration command.
|
|
2041
|
+
// It's being set to true before starting the calibration process.
|
|
2042
|
+
// This happens regardless of the shutter type. I didn't capture
|
|
2043
|
+
// any packages where this attribute is being actively set to false.
|
|
2044
|
+
// Maybe this activates some "full calibration" flag which is being
|
|
2045
|
+
// set to false by the device itself afterward?
|
|
2046
|
+
unknownAttributeTwo: { ID: 0x002a, type: zigbee_herdsman_1.Zcl.DataType.BOOLEAN },
|
|
2047
|
+
},
|
|
2048
|
+
commands: {
|
|
2049
|
+
// Command being sent by the Bosch SHC when starting an
|
|
2050
|
+
// automatic shutter calibration.
|
|
2051
|
+
startAutomaticMotorCalibration: { ID: 0x00, parameters: [] },
|
|
1850
2052
|
},
|
|
1851
|
-
commands: {},
|
|
1852
2053
|
commandsResponse: {},
|
|
1853
2054
|
}),
|
|
1854
2055
|
boschExtend.bmct(),
|
|
2056
|
+
boschExtend.seMeteringCluster(),
|
|
2057
|
+
boschExtend.resetEnergyReading(),
|
|
1855
2058
|
],
|
|
1856
2059
|
ota: true,
|
|
1857
2060
|
configure: async (device, coordinatorEndpoint) => {
|
|
2061
|
+
const lightConfiguration = async () => {
|
|
2062
|
+
const endpoint1 = device.getEndpoint(1);
|
|
2063
|
+
await reporting.bind(endpoint1, coordinatorEndpoint, ["genIdentify"]);
|
|
2064
|
+
await endpoint1.read("boschSpecific", ["switchType"]);
|
|
2065
|
+
const endpoint2 = device.getEndpoint(2);
|
|
2066
|
+
await reporting.bind(endpoint2, coordinatorEndpoint, ["genIdentify", "genOnOff", "boschSpecific"]);
|
|
2067
|
+
await reporting.onOff(endpoint2);
|
|
2068
|
+
await endpoint2.read("genOnOff", ["onOff", "startUpOnOff"]);
|
|
2069
|
+
await endpoint2.read("boschSpecific", [
|
|
2070
|
+
"switchMode",
|
|
2071
|
+
"childLock",
|
|
2072
|
+
"autoOffEnabled",
|
|
2073
|
+
"autoOffTime",
|
|
2074
|
+
]);
|
|
2075
|
+
const endpoint3 = device.getEndpoint(3);
|
|
2076
|
+
await reporting.bind(endpoint3, coordinatorEndpoint, ["genIdentify", "genOnOff", "boschSpecific"]);
|
|
2077
|
+
await reporting.onOff(endpoint3);
|
|
2078
|
+
await endpoint3.read("genOnOff", ["onOff", "startUpOnOff"]);
|
|
2079
|
+
await endpoint3.read("boschSpecific", [
|
|
2080
|
+
"switchMode",
|
|
2081
|
+
"childLock",
|
|
2082
|
+
"autoOffEnabled",
|
|
2083
|
+
"autoOffTime",
|
|
2084
|
+
]);
|
|
2085
|
+
};
|
|
2086
|
+
const shutterConfiguration = async () => {
|
|
2087
|
+
const endpoint1 = device.getEndpoint(1);
|
|
2088
|
+
await reporting.bind(endpoint1, coordinatorEndpoint, ["genIdentify", "closuresWindowCovering", "boschSpecific"]);
|
|
2089
|
+
await reporting.currentPositionLiftPercentage(endpoint1);
|
|
2090
|
+
await endpoint1.read("closuresWindowCovering", ["currentPositionLiftPercentage"]);
|
|
2091
|
+
const payloadMotorState = (0, reporting_1.payload)("motorState", 0, constants_1.repInterval.MAX, 0);
|
|
2092
|
+
await endpoint1.configureReporting("boschSpecific", payloadMotorState);
|
|
2093
|
+
await endpoint1.read("boschSpecific", [
|
|
2094
|
+
"switchType",
|
|
2095
|
+
"switchMode",
|
|
2096
|
+
"motorState",
|
|
2097
|
+
"calibrationOpeningTime",
|
|
2098
|
+
"calibrationClosingTime",
|
|
2099
|
+
"calibrationButtonHoldTime",
|
|
2100
|
+
"calibrationMotorStartDelay",
|
|
2101
|
+
"childLock",
|
|
2102
|
+
]);
|
|
2103
|
+
};
|
|
1858
2104
|
const endpoint1 = device.getEndpoint(1);
|
|
1859
|
-
await
|
|
1860
|
-
await
|
|
1861
|
-
await
|
|
1862
|
-
"deviceMode",
|
|
1863
|
-
"switchType",
|
|
1864
|
-
"motorState",
|
|
1865
|
-
"childLock",
|
|
1866
|
-
"calibrationOpeningTime",
|
|
1867
|
-
"calibrationClosingTime",
|
|
1868
|
-
"calibrationButtonHoldTime",
|
|
1869
|
-
"calibrationMotorStartDelay",
|
|
1870
|
-
]);
|
|
1871
|
-
const endpoint2 = device.getEndpoint(2);
|
|
1872
|
-
await endpoint2.read("boschSpecific", ["childLock"]);
|
|
1873
|
-
await reporting.bind(endpoint2, coordinatorEndpoint, ["genIdentify", "genOnOff"]);
|
|
1874
|
-
await reporting.onOff(endpoint2);
|
|
1875
|
-
const endpoint3 = device.getEndpoint(3);
|
|
1876
|
-
await endpoint3.read("boschSpecific", ["childLock"]);
|
|
1877
|
-
await reporting.bind(endpoint3, coordinatorEndpoint, ["genIdentify", "genOnOff"]);
|
|
1878
|
-
await reporting.onOff(endpoint3);
|
|
2105
|
+
await endpoint1.read("boschSpecific", ["deviceMode"]);
|
|
2106
|
+
await lightConfiguration();
|
|
2107
|
+
await shutterConfiguration();
|
|
1879
2108
|
},
|
|
1880
2109
|
exposes: (device, options) => {
|
|
1881
2110
|
const stateDeviceMode = {
|
|
@@ -1893,63 +2122,159 @@ exports.definitions = [
|
|
|
1893
2122
|
button_key_change: 0x02,
|
|
1894
2123
|
rocker_switch: 0x03,
|
|
1895
2124
|
rocker_switch_key_change: 0x04,
|
|
2125
|
+
none: 0x00,
|
|
2126
|
+
};
|
|
2127
|
+
const stateSwitchMode = {
|
|
2128
|
+
coupled: 0x00,
|
|
2129
|
+
decoupled: 0x01,
|
|
2130
|
+
only_short_press_decoupled: 0x02,
|
|
2131
|
+
only_long_press_decoupled: 0x03,
|
|
2132
|
+
};
|
|
2133
|
+
const commonExposes = (switchType) => {
|
|
2134
|
+
const exposeList = [];
|
|
2135
|
+
exposeList.push(e.enum("switch_type", ea.ALL, Object.keys(stateSwitchType)).withDescription("Module controlled by a rocker switch or a button"));
|
|
2136
|
+
if (switchType !== "none") {
|
|
2137
|
+
let supportedActionTypes;
|
|
2138
|
+
switch (switchType) {
|
|
2139
|
+
case "button":
|
|
2140
|
+
case "button_key_change":
|
|
2141
|
+
supportedActionTypes = [
|
|
2142
|
+
"press_released_left",
|
|
2143
|
+
"press_released_right",
|
|
2144
|
+
"hold_left",
|
|
2145
|
+
"hold_right",
|
|
2146
|
+
"hold_released_left",
|
|
2147
|
+
"hold_released_right",
|
|
2148
|
+
];
|
|
2149
|
+
break;
|
|
2150
|
+
case "rocker_switch":
|
|
2151
|
+
case "rocker_switch_key_change":
|
|
2152
|
+
supportedActionTypes = ["opened_left", "opened_right", "closed_left", "closed_right"];
|
|
2153
|
+
break;
|
|
2154
|
+
}
|
|
2155
|
+
exposeList.push(e.action(supportedActionTypes), e.action_duration());
|
|
2156
|
+
}
|
|
2157
|
+
return exposeList;
|
|
2158
|
+
};
|
|
2159
|
+
const lightExposes = (endpoint, switchType) => {
|
|
2160
|
+
const exposeList = [];
|
|
2161
|
+
exposeList.push(e.switch().withEndpoint(endpoint), e.power_on_behavior().withEndpoint(endpoint), e
|
|
2162
|
+
.binary("auto_off_enabled", ea.ALL, "ON", "OFF")
|
|
2163
|
+
.withEndpoint(endpoint)
|
|
2164
|
+
.withDescription("Enable/Disable the automatic turn-off feature"), e
|
|
2165
|
+
.numeric("auto_off_time", ea.ALL)
|
|
2166
|
+
.withValueMin(0)
|
|
2167
|
+
.withValueMax(720)
|
|
2168
|
+
.withValueStep(1)
|
|
2169
|
+
.withUnit("min")
|
|
2170
|
+
.withDescription("Turn off the output after the specified amount of time. Only in action when the automatic turn-off is enabled.")
|
|
2171
|
+
.withEndpoint(endpoint));
|
|
2172
|
+
if (switchType !== "none") {
|
|
2173
|
+
let supportedSwitchModes;
|
|
2174
|
+
switch (switchType) {
|
|
2175
|
+
case "button":
|
|
2176
|
+
case "button_key_change":
|
|
2177
|
+
supportedSwitchModes = Object.keys(stateSwitchMode);
|
|
2178
|
+
break;
|
|
2179
|
+
case "rocker_switch":
|
|
2180
|
+
case "rocker_switch_key_change":
|
|
2181
|
+
supportedSwitchModes = Object.keys(stateSwitchMode).filter((switchMode) => switchMode === "coupled" || switchMode === "decoupled");
|
|
2182
|
+
break;
|
|
2183
|
+
}
|
|
2184
|
+
exposeList.push(e
|
|
2185
|
+
.enum("switch_mode", ea.ALL, supportedSwitchModes)
|
|
2186
|
+
.withEndpoint(endpoint)
|
|
2187
|
+
.withDescription("Decouple the switch from the corresponding output to use it for other purposes. Please keep in mind that the available options depend on the used switch type."), e.binary("child_lock", ea.ALL, "ON", "OFF").withEndpoint(endpoint).withDescription("Enable/Disable child lock"));
|
|
2188
|
+
}
|
|
2189
|
+
return exposeList;
|
|
1896
2190
|
};
|
|
1897
|
-
const
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
const lightExposes = [
|
|
1901
|
-
e.switch().withEndpoint("left"),
|
|
1902
|
-
e.switch().withEndpoint("right"),
|
|
1903
|
-
e.power_on_behavior().withEndpoint("left"),
|
|
1904
|
-
e.power_on_behavior().withEndpoint("right"),
|
|
1905
|
-
e.binary("child_lock", ea.ALL, "ON", "OFF").withEndpoint("left").withDescription("Enable/Disable child lock"),
|
|
1906
|
-
e.binary("child_lock", ea.ALL, "ON", "OFF").withEndpoint("right").withDescription("Enable/Disable child lock"),
|
|
1907
|
-
];
|
|
1908
|
-
const coverExposes = [
|
|
1909
|
-
e.cover_position(),
|
|
1910
|
-
e.enum("motor_state", ea.STATE, Object.keys(stateMotor)).withDescription("Current shutter motor state"),
|
|
1911
|
-
e.binary("child_lock", ea.ALL, "ON", "OFF").withDescription("Enable/Disable child lock"),
|
|
1912
|
-
e
|
|
2191
|
+
const coverExposes = (switchType) => {
|
|
2192
|
+
const exposeList = [];
|
|
2193
|
+
exposeList.push(e.cover_position(), e.enum("motor_state", ea.STATE, Object.keys(stateMotor)).withDescription("Current shutter motor state"), e
|
|
1913
2194
|
.numeric("calibration_closing_time", ea.ALL)
|
|
1914
2195
|
.withUnit("s")
|
|
1915
2196
|
.withDescription("Calibrate shutter closing time")
|
|
1916
2197
|
.withValueMin(1)
|
|
1917
2198
|
.withValueMax(90)
|
|
1918
|
-
.withValueStep(0.1),
|
|
1919
|
-
e
|
|
2199
|
+
.withValueStep(0.1), e
|
|
1920
2200
|
.numeric("calibration_opening_time", ea.ALL)
|
|
1921
2201
|
.withUnit("s")
|
|
1922
2202
|
.withDescription("Calibrate shutter opening time")
|
|
1923
2203
|
.withValueMin(1)
|
|
1924
2204
|
.withValueMax(90)
|
|
1925
|
-
.withValueStep(0.1),
|
|
1926
|
-
e
|
|
2205
|
+
.withValueStep(0.1), e
|
|
1927
2206
|
.numeric("calibration_button_hold_time", ea.ALL)
|
|
1928
2207
|
.withUnit("s")
|
|
1929
2208
|
.withDescription("Time to hold for long press")
|
|
1930
2209
|
.withValueMin(0.1)
|
|
1931
2210
|
.withValueMax(2)
|
|
1932
|
-
.withValueStep(0.1),
|
|
1933
|
-
e
|
|
2211
|
+
.withValueStep(0.1), e
|
|
1934
2212
|
.numeric("calibration_motor_start_delay", ea.ALL)
|
|
1935
2213
|
.withUnit("s")
|
|
1936
2214
|
.withDescription("Delay between command and motor start")
|
|
1937
2215
|
.withValueMin(0)
|
|
1938
2216
|
.withValueMax(20)
|
|
1939
|
-
.withValueStep(0.1)
|
|
1940
|
-
|
|
2217
|
+
.withValueStep(0.1));
|
|
2218
|
+
if (switchType !== "none") {
|
|
2219
|
+
let supportedSwitchModes;
|
|
2220
|
+
switch (switchType) {
|
|
2221
|
+
case "button":
|
|
2222
|
+
case "button_key_change":
|
|
2223
|
+
supportedSwitchModes = Object.keys(stateSwitchMode).filter((switchMode) => switchMode === "coupled" || switchMode === "only_long_press_decoupled");
|
|
2224
|
+
break;
|
|
2225
|
+
case "rocker_switch":
|
|
2226
|
+
case "rocker_switch_key_change":
|
|
2227
|
+
supportedSwitchModes = Object.keys(stateSwitchMode).filter((switchMode) => switchMode === "coupled");
|
|
2228
|
+
break;
|
|
2229
|
+
}
|
|
2230
|
+
exposeList.push(e
|
|
2231
|
+
.enum("switch_mode", ea.ALL, supportedSwitchModes)
|
|
2232
|
+
.withDescription("Decouple the switch from the corresponding output to use it for other purposes. Please keep in mind that the available options depend on the used switch type."), e.binary("child_lock", ea.ALL, "ON", "OFF").withDescription("Enable/Disable child lock"));
|
|
2233
|
+
}
|
|
2234
|
+
return exposeList;
|
|
2235
|
+
};
|
|
1941
2236
|
if (!utils.isDummyDevice(device)) {
|
|
1942
2237
|
const deviceModeKey = device.getEndpoint(1).getClusterAttributeValue("boschSpecific", "deviceMode");
|
|
1943
2238
|
const deviceMode = Object.keys(stateDeviceMode).find((key) => stateDeviceMode[key] === deviceModeKey);
|
|
2239
|
+
const switchTypeKey = device.getEndpoint(1).getClusterAttributeValue("boschSpecific", "switchType");
|
|
2240
|
+
const switchType = Object.keys(stateSwitchType).find((key) => stateSwitchType[key] === switchTypeKey);
|
|
1944
2241
|
if (deviceMode === "light") {
|
|
1945
|
-
return [...commonExposes, ...lightExposes];
|
|
2242
|
+
return [...commonExposes(switchType), ...lightExposes("left", switchType), ...lightExposes("right", switchType)];
|
|
1946
2243
|
}
|
|
1947
2244
|
if (deviceMode === "shutter") {
|
|
1948
|
-
return [...commonExposes, ...coverExposes];
|
|
2245
|
+
return [...commonExposes(switchType), ...coverExposes(switchType)];
|
|
1949
2246
|
}
|
|
1950
2247
|
}
|
|
1951
2248
|
return [e.enum("device_mode", ea.ALL, Object.keys(stateDeviceMode)).withDescription("Device mode")];
|
|
1952
2249
|
},
|
|
2250
|
+
onEvent: (event) => {
|
|
2251
|
+
if (event.type !== "deviceInterview") {
|
|
2252
|
+
return;
|
|
2253
|
+
}
|
|
2254
|
+
// During interview, the Bosch BMCT-SLZ is requesting
|
|
2255
|
+
// the zclVersion attribute from the coordinator. As
|
|
2256
|
+
// Z2M doesn't know the zclVersion of the device yet,
|
|
2257
|
+
// the request is left unanswered. This makes the device
|
|
2258
|
+
// believe it dropped out of network every 10 minutes which
|
|
2259
|
+
// not only generates unnecessary network congestion, but
|
|
2260
|
+
// makes the LED on the device blink during that sequence
|
|
2261
|
+
// as well. To prevent that, we have to manually answer
|
|
2262
|
+
// the zclVersion request at the earliest possible stage
|
|
2263
|
+
// and mimic the answer from the Bosch SHC II.
|
|
2264
|
+
event.data.device.customReadResponse = (frame, endpoint) => {
|
|
2265
|
+
const isZclVersionRequest = frame.isCluster("genBasic") && frame.payload.find((i) => i.attrId === 0);
|
|
2266
|
+
if (!isZclVersionRequest) {
|
|
2267
|
+
return false;
|
|
2268
|
+
}
|
|
2269
|
+
const payload = {
|
|
2270
|
+
zclVersion: 1,
|
|
2271
|
+
};
|
|
2272
|
+
endpoint.readResponse(frame.cluster.name, frame.header.transactionSequenceNumber, payload).catch((e) => {
|
|
2273
|
+
logger_1.logger.warning(`Custom zclVersion response failed for '${event.data.device.ieeeAddr}': ${e}`, NS);
|
|
2274
|
+
});
|
|
2275
|
+
return true;
|
|
2276
|
+
};
|
|
2277
|
+
},
|
|
1953
2278
|
},
|
|
1954
2279
|
{
|
|
1955
2280
|
zigbeeModel: ["RBSH-US4BTN-ZB-EU"],
|