zigbee-herdsman-converters 25.8.0 → 25.10.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 CHANGED
@@ -1,5 +1,41 @@
1
1
  # Changelog
2
2
 
3
+ ## [25.10.0](https://github.com/Koenkk/zigbee-herdsman-converters/compare/v25.9.0...v25.10.0) (2025-08-27)
4
+
5
+
6
+ ### Features
7
+
8
+ * **add:** 929003808801 ([#9853](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9853)) ([e1e7f12](https://github.com/Koenkk/zigbee-herdsman-converters/commit/e1e7f12bf5210a9a363ee46451908f6c0f5207b7))
9
+ * **add:** 929004297402 https://github.com/Koenkk/zigbee2mqtt/issues/28397 ([d238f93](https://github.com/Koenkk/zigbee-herdsman-converters/commit/d238f93bb8934fc51fe43a895d0b6adc09c1423c))
10
+ * **add:** AirQ_Monitor_S01 ([#9841](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9841)) ([ff8b222](https://github.com/Koenkk/zigbee-herdsman-converters/commit/ff8b222636a76ee0ce98e1cc11bbcfeb601934d9))
11
+ * **add:** FUT068ZR ([#9859](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9859)) ([56105e0](https://github.com/Koenkk/zigbee-herdsman-converters/commit/56105e0016f4e9e695998c91ba2161f8c2cbbf73))
12
+ * 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))
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * 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))
18
+ * 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))
19
+ * 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))
20
+ * **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))
21
+ * 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))
22
+ * Various code fixes ([#9854](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9854)) ([7ba943e](https://github.com/Koenkk/zigbee-herdsman-converters/commit/7ba943e6abaa9978c00d048dfeac80d3721fd4ae))
23
+
24
+ ## [25.9.0](https://github.com/Koenkk/zigbee-herdsman-converters/compare/v25.8.0...v25.9.0) (2025-08-25)
25
+
26
+
27
+ ### Features
28
+
29
+ * **add:** OSM-300ZB ([#9846](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9846)) ([8868ede](https://github.com/Koenkk/zigbee-herdsman-converters/commit/8868ede0e1702cde132412c3e8d79dc943f2e339))
30
+ * **add:** T2_E26 https://github.com/Koenkk/zigbee2mqtt/issues/26182 ([73b5ddd](https://github.com/Koenkk/zigbee-herdsman-converters/commit/73b5dddc5a6024fcc912d3159940cbedfe771269))
31
+ * SONOFF SNZB-05P: support OTA https://github.com/Koenkk/zigbee-OTA/issues/838 ([4efaf65](https://github.com/Koenkk/zigbee-herdsman-converters/commit/4efaf65aed6c798d8a84615a81e9e535e7ad70cb))
32
+
33
+
34
+ ### Bug Fixes
35
+
36
+ * Girier JR-ZPM01: fix configure failing https://github.com/Koenkk/zigbee2mqtt/issues/28198 ([5a7f235](https://github.com/Koenkk/zigbee-herdsman-converters/commit/5a7f2352e34d0e6ec02c3bfd03dd31cf62253efe))
37
+ * **ignore:** bump the minor-patch group with 2 updates ([#9845](https://github.com/Koenkk/zigbee-herdsman-converters/issues/9845)) ([3f8e0f5](https://github.com/Koenkk/zigbee-herdsman-converters/commit/3f8e0f5ea15e277099d433ee06ba10dc71bf20e6))
38
+
3
39
  ## [25.8.0](https://github.com/Koenkk/zigbee-herdsman-converters/compare/v25.7.0...v25.8.0) (2025-08-24)
4
40
 
5
41
 
@@ -1 +1 @@
1
- {"version":3,"file":"bosch.d.ts","sourceRoot":"","sources":["../../src/devices/bosch.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAC,oBAAoB,EAAyC,MAAM,cAAc,CAAC;AAwuC/F,eAAO,MAAM,WAAW,EAAE,oBAAoB,EA68B7C,CAAC"}
1
+ {"version":3,"file":"bosch.d.ts","sourceRoot":"","sources":["../../src/devices/bosch.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAC,oBAAoB,EAAyC,MAAM,cAAc,CAAC;AAk3C/F,eAAO,MAAM,WAAW,EAAE,oBAAoB,EAwoC7C,CAAC"}
@@ -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.on_off,
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"],
@@ -821,6 +893,10 @@ const boschExtend = {
821
893
  if (data.switchType !== undefined) {
822
894
  result.switch_type = Object.keys(stateSwitchType).find((key) => stateSwitchType[key] === msg.data.switchType);
823
895
  }
896
+ if (data.switchMode !== undefined) {
897
+ const property = utils.postfixWithEndpointName("switch_mode", msg, model, meta);
898
+ result[property] = Object.keys(stateSwitchMode).find((key) => stateSwitchMode[key] === msg.data.switchMode);
899
+ }
824
900
  if (data.calibrationOpeningTime !== undefined) {
825
901
  result.calibration_opening_time = msg.data.calibrationOpeningTime / 10;
826
902
  }
@@ -840,6 +916,14 @@ const boschExtend = {
840
916
  if (data.motorState !== undefined) {
841
917
  result.motor_state = Object.keys(stateMotor).find((key) => stateMotor[key] === msg.data.motorState);
842
918
  }
919
+ if (data.autoOffEnabled !== undefined) {
920
+ const property = utils.postfixWithEndpointName("auto_off_enabled", msg, model, meta);
921
+ result[property] = msg.data.autoOffEnabled === 1 ? "ON" : "OFF";
922
+ }
923
+ if (data.autoOffTime !== undefined) {
924
+ const property = utils.postfixWithEndpointName("auto_off_time", msg, model, meta);
925
+ result[property] = msg.data.autoOffTime / 60;
926
+ }
843
927
  return result;
844
928
  },
845
929
  },
@@ -848,7 +932,17 @@ const boschExtend = {
848
932
  tz.power_on_behavior,
849
933
  tz.cover_position_tilt,
850
934
  {
851
- key: ["device_mode", "switch_type", "child_lock", "state", "on_time", "off_wait_time"],
935
+ key: [
936
+ "device_mode",
937
+ "switch_type",
938
+ "switch_mode",
939
+ "child_lock",
940
+ "state",
941
+ "on_time",
942
+ "off_wait_time",
943
+ "auto_off_enabled",
944
+ "auto_off_time",
945
+ ],
852
946
  convertSet: async (entity, key, value, meta) => {
853
947
  if (key === "state") {
854
948
  if ("ID" in entity && entity.ID === 1) {
@@ -874,11 +968,25 @@ const boschExtend = {
874
968
  await entity.write("boschSpecific", { switchType: index });
875
969
  return { state: { switch_type: value } };
876
970
  }
971
+ if (key === "switch_mode") {
972
+ const index = utils.getFromLookup(value, stateSwitchMode);
973
+ await entity.write("boschSpecific", { switchMode: index });
974
+ return { state: { switch_mode: value } };
975
+ }
877
976
  if (key === "child_lock") {
878
977
  const index = utils.getFromLookup(value, stateOffOn);
879
978
  await entity.write("boschSpecific", { childLock: index });
880
979
  return { state: { child_lock: value } };
881
980
  }
981
+ if (key === "auto_off_enabled") {
982
+ const index = utils.getFromLookup(value, stateOffOn);
983
+ await entity.write("boschSpecific", { autoOffEnabled: index });
984
+ return { state: { auto_off_enabled: value } };
985
+ }
986
+ if (key === "auto_off_time" && typeof value === "number") {
987
+ await entity.write("boschSpecific", { autoOffTime: value * 60 });
988
+ return { state: { auto_off_time: value } };
989
+ }
882
990
  },
883
991
  convertGet: async (entity, key, meta) => {
884
992
  switch (key) {
@@ -895,9 +1003,18 @@ const boschExtend = {
895
1003
  case "switch_type":
896
1004
  await entity.read("boschSpecific", ["switchType"]);
897
1005
  break;
1006
+ case "switch_mode":
1007
+ await entity.read("boschSpecific", ["switchMode"]);
1008
+ break;
898
1009
  case "child_lock":
899
1010
  await entity.read("boschSpecific", ["childLock"]);
900
1011
  break;
1012
+ case "auto_off_enabled":
1013
+ await entity.read("boschSpecific", ["autoOffEnabled"]);
1014
+ break;
1015
+ case "auto_off_time":
1016
+ await entity.read("boschSpecific", ["autoOffTime"]);
1017
+ break;
901
1018
  default:
902
1019
  throw new Error(`Unhandled key boschExtend.bmct.toZigbee.convertGet ${key}`);
903
1020
  }
@@ -1834,48 +1951,130 @@ exports.definitions = [
1834
1951
  description: "Light/shutter control unit II",
1835
1952
  extend: [
1836
1953
  m.deviceEndpoints({ endpoints: { left: 2, right: 3 } }),
1837
- m.electricityMeter({ voltage: false, current: false }),
1954
+ m.electricityMeter({
1955
+ voltage: false,
1956
+ current: false,
1957
+ power: { change: 1 },
1958
+ energy: { change: 1 },
1959
+ }),
1838
1960
  m.deviceAddCustomCluster("boschSpecific", {
1839
1961
  ID: 0xfca0,
1840
1962
  manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
1841
1963
  attributes: {
1842
1964
  deviceMode: { ID: 0x0000, type: zigbee_herdsman_1.Zcl.DataType.ENUM8 },
1843
1965
  switchType: { ID: 0x0001, type: zigbee_herdsman_1.Zcl.DataType.ENUM8 },
1966
+ switchMode: { ID: 0x0031, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
1844
1967
  calibrationOpeningTime: { ID: 0x0002, type: zigbee_herdsman_1.Zcl.DataType.UINT32 },
1845
1968
  calibrationClosingTime: { ID: 0x0003, type: zigbee_herdsman_1.Zcl.DataType.UINT32 },
1969
+ // 0x0005 isn't used at all when using the Bosch SHC as of 30-06-2025.
1970
+ // As I don't have any shutters, I can't run all calibration steps
1971
+ // successfully. So, keep any comments regarding these
1972
+ // attributes with caution.
1846
1973
  calibrationButtonHoldTime: { ID: 0x0005, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
1974
+ autoOffEnabled: { ID: 0x0006, type: zigbee_herdsman_1.Zcl.DataType.BOOLEAN },
1975
+ autoOffTime: { ID: 0x0007, type: zigbee_herdsman_1.Zcl.DataType.UINT16 },
1847
1976
  childLock: { ID: 0x0008, type: zigbee_herdsman_1.Zcl.DataType.BOOLEAN },
1977
+ // 0x000f is only being set when using the automatic calibration.
1978
+ // It's being set to 0 then before sending the calibration
1979
+ // command. Additionally, when changing
1980
+ // the calibrationOpeningTime or calibrationClosingTime in the
1981
+ // Bosch app, it's also being set to 0.
1982
+ // I couldn't find any way to set 0x000f manually in the Bosch app.
1848
1983
  calibrationMotorStartDelay: { ID: 0x000f, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
1984
+ calibrationMotorReverseDirection: { ID: 0x0032, type: zigbee_herdsman_1.Zcl.DataType.BOOLEAN },
1849
1985
  motorState: { ID: 0x0013, type: zigbee_herdsman_1.Zcl.DataType.ENUM8 },
1986
+ // unknownAttributeOne is always being configured as reporting
1987
+ // attribute on endpoint 1 when using the Bosch SHC.
1988
+ // Can't tell what this attribute does (always received
1989
+ // 0x00 as answer on manual lookup).
1990
+ unknownAttributeOne: { ID: 0x0004, type: zigbee_herdsman_1.Zcl.DataType.BITMAP8 },
1991
+ // Attribute is being set to 255 when deactivating the automatic
1992
+ // detection of the motor end position by the Bosch SHC. After
1993
+ // activating the automatic end position detection it's being set
1994
+ // to 0 by the Bosch SHC. Apart from that, there's no way to manually
1995
+ // change the value.
1996
+ calibrationMotorEndPosition: { ID: 0x0021, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
1997
+ // 0x0033 is used when setting the motor start delay manually
1998
+ // using the Bosch SHC as of 30-06-2025.
1999
+ // If the user wants to automatically detect the delay during
2000
+ // calibration, it's being set to 0 over the Bosch app.
2001
+ calibrationNewMotorStartDelay: { ID: 0x0033, type: zigbee_herdsman_1.Zcl.DataType.UINT16 },
2002
+ // 0x0010 and 0x0011 is being set simultaneously with the same value
2003
+ // when changing the delay for the rotation of the slats on venetian
2004
+ // blinds. Maybe one attribute for each direction?
2005
+ // It's also being configured as reporting attribute when using
2006
+ // venetian blinds.
2007
+ slatRotationDurationOne: { ID: 0x0010, type: zigbee_herdsman_1.Zcl.DataType.UINT32 },
2008
+ slatRotationDurationTwo: { ID: 0x0011, type: zigbee_herdsman_1.Zcl.DataType.UINT32 },
2009
+ // 0x002a is only being used when doing an automatic calibration
2010
+ // with the Bosch specific startAutomaticMotorCalibration command.
2011
+ // It's being set to true before starting the calibration process.
2012
+ // This happens regardless of the shutter type. I didn't capture
2013
+ // any packages where this attribute is being actively set to false.
2014
+ // Maybe this activates some "full calibration" flag which is being
2015
+ // set to false by the device itself afterward?
2016
+ unknownAttributeTwo: { ID: 0x002a, type: zigbee_herdsman_1.Zcl.DataType.BOOLEAN },
2017
+ },
2018
+ commands: {
2019
+ // Command being sent by the Bosch SHC when starting an
2020
+ // automatic shutter calibration.
2021
+ startAutomaticMotorCalibration: { ID: 0x00, parameters: [] },
1850
2022
  },
1851
- commands: {},
1852
2023
  commandsResponse: {},
1853
2024
  }),
1854
2025
  boschExtend.bmct(),
2026
+ boschExtend.seMeteringCluster(),
2027
+ boschExtend.resetEnergyReading(),
1855
2028
  ],
1856
2029
  ota: true,
1857
2030
  configure: async (device, coordinatorEndpoint) => {
2031
+ const lightConfiguration = async () => {
2032
+ const endpoint1 = device.getEndpoint(1);
2033
+ await reporting.bind(endpoint1, coordinatorEndpoint, ["genIdentify"]);
2034
+ await endpoint1.read("boschSpecific", ["switchType"]);
2035
+ const endpoint2 = device.getEndpoint(2);
2036
+ await reporting.bind(endpoint2, coordinatorEndpoint, ["genIdentify", "genOnOff", "boschSpecific"]);
2037
+ await reporting.onOff(endpoint2);
2038
+ await endpoint2.read("genOnOff", ["onOff", "startUpOnOff"]);
2039
+ await endpoint2.read("boschSpecific", [
2040
+ "switchMode",
2041
+ "childLock",
2042
+ "autoOffEnabled",
2043
+ "autoOffTime",
2044
+ ]);
2045
+ const endpoint3 = device.getEndpoint(3);
2046
+ await reporting.bind(endpoint3, coordinatorEndpoint, ["genIdentify", "genOnOff", "boschSpecific"]);
2047
+ await reporting.onOff(endpoint3);
2048
+ await endpoint3.read("genOnOff", ["onOff", "startUpOnOff"]);
2049
+ await endpoint3.read("boschSpecific", [
2050
+ "switchMode",
2051
+ "childLock",
2052
+ "autoOffEnabled",
2053
+ "autoOffTime",
2054
+ ]);
2055
+ };
2056
+ const shutterConfiguration = async () => {
2057
+ const endpoint1 = device.getEndpoint(1);
2058
+ await reporting.bind(endpoint1, coordinatorEndpoint, ["genIdentify", "closuresWindowCovering", "boschSpecific"]);
2059
+ await reporting.currentPositionLiftPercentage(endpoint1);
2060
+ await endpoint1.read("closuresWindowCovering", ["currentPositionLiftPercentage"]);
2061
+ const payloadMotorState = (0, reporting_1.payload)("motorState", 0, constants_1.repInterval.MAX, 0);
2062
+ await endpoint1.configureReporting("boschSpecific", payloadMotorState);
2063
+ await endpoint1.read("boschSpecific", [
2064
+ "switchType",
2065
+ "switchMode",
2066
+ "motorState",
2067
+ "calibrationOpeningTime",
2068
+ "calibrationClosingTime",
2069
+ "calibrationButtonHoldTime",
2070
+ "calibrationMotorStartDelay",
2071
+ "childLock",
2072
+ ]);
2073
+ };
1858
2074
  const endpoint1 = device.getEndpoint(1);
1859
- await reporting.bind(endpoint1, coordinatorEndpoint, ["genIdentify", "closuresWindowCovering", "boschSpecific"]);
1860
- await reporting.currentPositionLiftPercentage(endpoint1);
1861
- await endpoint1.read("boschSpecific", [
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);
2075
+ await endpoint1.read("boschSpecific", ["deviceMode"]);
2076
+ await lightConfiguration();
2077
+ await shutterConfiguration();
1879
2078
  },
1880
2079
  exposes: (device, options) => {
1881
2080
  const stateDeviceMode = {
@@ -1893,15 +2092,67 @@ exports.definitions = [
1893
2092
  button_key_change: 0x02,
1894
2093
  rocker_switch: 0x03,
1895
2094
  rocker_switch_key_change: 0x04,
2095
+ none: 0x00,
2096
+ };
2097
+ const stateSwitchMode = {
2098
+ coupled: 0x00,
2099
+ decoupled: 0x01,
2100
+ only_short_press_decoupled: 0x02,
2101
+ only_long_press_decoupled: 0x03,
1896
2102
  };
1897
2103
  const commonExposes = [
1898
2104
  e.enum("switch_type", ea.ALL, Object.keys(stateSwitchType)).withDescription("Module controlled by a rocker switch or a button"),
2105
+ e.action([
2106
+ "press_released_left",
2107
+ "press_released_right",
2108
+ "hold_left",
2109
+ "hold_right",
2110
+ "hold_released_left",
2111
+ "hold_released_right",
2112
+ "opened_left",
2113
+ "opened_right",
2114
+ "closed_left",
2115
+ "closed_right",
2116
+ ]),
2117
+ e.action_duration(),
1899
2118
  ];
1900
2119
  const lightExposes = [
1901
2120
  e.switch().withEndpoint("left"),
1902
2121
  e.switch().withEndpoint("right"),
1903
2122
  e.power_on_behavior().withEndpoint("left"),
1904
2123
  e.power_on_behavior().withEndpoint("right"),
2124
+ e
2125
+ .enum("switch_mode", ea.ALL, Object.keys(stateSwitchMode))
2126
+ .withEndpoint("left")
2127
+ .withDescription("Decouple the switch from the corresponding output to use it for other purposes. Please keep in mind that not all options may work depending on your switch type."),
2128
+ e
2129
+ .enum("switch_mode", ea.ALL, Object.keys(stateSwitchMode))
2130
+ .withEndpoint("right")
2131
+ .withDescription("Decouple the switch from the corresponding output to use it for other purposes. Please keep in mind that not all options may work depending on your switch type."),
2132
+ e
2133
+ .binary("auto_off_enabled", ea.ALL, "ON", "OFF")
2134
+ .withEndpoint("left")
2135
+ .withDescription("Enable/Disable the automatic turn-off feature"),
2136
+ e
2137
+ .binary("auto_off_enabled", ea.ALL, "ON", "OFF")
2138
+ .withEndpoint("right")
2139
+ .withDescription("Enable/Disable the automatic turn-off feature"),
2140
+ e
2141
+ .numeric("auto_off_time", ea.ALL)
2142
+ .withValueMin(0)
2143
+ .withValueMax(720)
2144
+ .withValueStep(1)
2145
+ .withUnit("min")
2146
+ .withDescription("Turn off the output after the specified amount of time. Only in action when the automatic turn-off is enabled.")
2147
+ .withEndpoint("left"),
2148
+ e
2149
+ .numeric("auto_off_time", ea.ALL)
2150
+ .withValueMin(0)
2151
+ .withValueMax(720)
2152
+ .withValueStep(1)
2153
+ .withUnit("min")
2154
+ .withDescription("Turn off the output after the specified amount of time. Only in action when the automatic turn-off is enabled.")
2155
+ .withEndpoint("right"),
1905
2156
  e.binary("child_lock", ea.ALL, "ON", "OFF").withEndpoint("left").withDescription("Enable/Disable child lock"),
1906
2157
  e.binary("child_lock", ea.ALL, "ON", "OFF").withEndpoint("right").withDescription("Enable/Disable child lock"),
1907
2158
  ];
@@ -1909,6 +2160,9 @@ exports.definitions = [
1909
2160
  e.cover_position(),
1910
2161
  e.enum("motor_state", ea.STATE, Object.keys(stateMotor)).withDescription("Current shutter motor state"),
1911
2162
  e.binary("child_lock", ea.ALL, "ON", "OFF").withDescription("Enable/Disable child lock"),
2163
+ e
2164
+ .enum("switch_mode", ea.ALL, Object.keys(stateSwitchMode).filter((switchMode) => switchMode === "coupled" || switchMode === "only_long_press_decoupled"))
2165
+ .withDescription("Decouple the switch from the corresponding output to use it for other purposes. Please keep in mind that not all options may work depending on your switch type."),
1912
2166
  e
1913
2167
  .numeric("calibration_closing_time", ea.ALL)
1914
2168
  .withUnit("s")
@@ -1950,6 +2204,34 @@ exports.definitions = [
1950
2204
  }
1951
2205
  return [e.enum("device_mode", ea.ALL, Object.keys(stateDeviceMode)).withDescription("Device mode")];
1952
2206
  },
2207
+ onEvent: (event) => {
2208
+ if (event.type !== "deviceInterview") {
2209
+ return;
2210
+ }
2211
+ // During interview, the Bosch BMCT-SLZ is requesting
2212
+ // the zclVersion attribute from the coordinator. As
2213
+ // Z2M doesn't know the zclVersion of the device yet,
2214
+ // the request is left unanswered. This makes the device
2215
+ // believe it dropped out of network every 10 minutes which
2216
+ // not only generates unnecessary network congestion, but
2217
+ // makes the LED on the device blink during that sequence
2218
+ // as well. To prevent that, we have to manually answer
2219
+ // the zclVersion request at the earliest possible stage
2220
+ // and mimic the answer from the Bosch SHC II.
2221
+ event.data.device.customReadResponse = (frame, endpoint) => {
2222
+ const isZclVersionRequest = frame.isCluster("genBasic") && frame.payload.find((i) => i.attrId === 0);
2223
+ if (!isZclVersionRequest) {
2224
+ return false;
2225
+ }
2226
+ const payload = {
2227
+ zclVersion: 1,
2228
+ };
2229
+ endpoint.readResponse(frame.cluster.name, frame.header.transactionSequenceNumber, payload).catch((e) => {
2230
+ logger_1.logger.warning(`Custom zclVersion response failed for '${event.data.device.ieeeAddr}': ${e}`, NS);
2231
+ });
2232
+ return true;
2233
+ };
2234
+ },
1953
2235
  },
1954
2236
  {
1955
2237
  zigbeeModel: ["RBSH-US4BTN-ZB-EU"],