zigbee-herdsman-converters 14.0.371 → 14.0.375

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.
@@ -5017,7 +5017,8 @@ const converters = {
5017
5017
  aqara_opple: {
5018
5018
  cluster: 'aqaraOpple',
5019
5019
  type: ['attributeReport', 'readResponse'],
5020
- options: [exposes.options.precision('temperature'), exposes.options.calibration('temperature')],
5020
+ options: [exposes.options.precision('temperature'), exposes.options.calibration('temperature'),
5021
+ exposes.options.precision('illuminance'), exposes.options.calibration('illuminance', 'percentual')],
5021
5022
  convert: (model, msg, publish, options, meta) => {
5022
5023
  const payload = {};
5023
5024
  if (msg.data.hasOwnProperty('247')) {
@@ -5142,6 +5143,8 @@ const converters = {
5142
5143
  if (['QBKG19LM', 'QBKG20LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM'].includes(model.model)) {
5143
5144
  const mapping = model.model === 'QBCZ15LM' ? 'usb' : 'right';
5144
5145
  payload[`state_${mapping}`] = value === 1 ? 'ON' : 'OFF';
5146
+ } else if (['RTCGQ12LM'].includes(model.model)) {
5147
+ payload.illuminance = calibrateAndPrecisionRoundOptions(value, options, 'illuminance');
5145
5148
  }
5146
5149
  } else if (index === 149) {
5147
5150
  payload.energy = precisionRound(value, 2); // 0x95
@@ -5310,6 +5313,65 @@ const converters = {
5310
5313
  return payload;
5311
5314
  },
5312
5315
  },
5316
+ RTCGQ12LM_occupancy_illuminance: {
5317
+ cluster: 'aqaraOpple',
5318
+ type: ['attributeReport', 'readResponse'],
5319
+ options: [exposes.options.precision('illuminance'), exposes.options.calibration('illuminance', 'percentual')],
5320
+ convert: (model, msg, publish, options, meta) => {
5321
+ if (msg.data.hasOwnProperty('illuminance')) {
5322
+ // The occupancy sensor only sends a message when motion detected.
5323
+ // Therefore we need to publish the no_motion detected by ourselves.
5324
+ const timeout = meta && meta.state && meta.state.hasOwnProperty('detection_interval') ? meta.state.detection_interval : 60;
5325
+
5326
+ // Stop existing timers because motion is detected and set a new one.
5327
+ globalStore.getValue(msg.endpoint, 'timers', []).forEach((t) => clearTimeout(t));
5328
+ globalStore.putValue(msg.endpoint, 'timers', []);
5329
+
5330
+ if (timeout !== 0) {
5331
+ const timer = setTimeout(() => {
5332
+ publish({occupancy: false});
5333
+ }, timeout * 1000);
5334
+
5335
+ globalStore.getValue(msg.endpoint, 'timers').push(timer);
5336
+ }
5337
+
5338
+ const illuminance = msg.data['illuminance'] - 65536;
5339
+ return {occupancy: true, illuminance: calibrateAndPrecisionRoundOptions(illuminance, options, 'illuminance')};
5340
+ }
5341
+ },
5342
+ },
5343
+ RTCGQ13LM_occupancy: {
5344
+ // This is for occupancy sensor that only send a message when motion detected,
5345
+ // but do not send a motion stop.
5346
+ // Therefore we need to publish the no_motion detected by ourselves.
5347
+ cluster: 'msOccupancySensing',
5348
+ type: ['attributeReport', 'readResponse'],
5349
+ convert: (model, msg, publish, options, meta) => {
5350
+ if (msg.data.occupancy !== 1) {
5351
+ // In case of 0 no occupancy is reported.
5352
+ // https://github.com/Koenkk/zigbee2mqtt/issues/467
5353
+ return;
5354
+ }
5355
+
5356
+ // The occupancy sensor only sends a message when motion detected.
5357
+ // Therefore we need to publish the no_motion detected by ourselves.
5358
+ const timeout = meta && meta.state && meta.state.hasOwnProperty('detection_interval') ? meta.state.detection_interval : 60;
5359
+
5360
+ // Stop existing timers because motion is detected and set a new one.
5361
+ globalStore.getValue(msg.endpoint, 'timers', []).forEach((t) => clearTimeout(t));
5362
+ globalStore.putValue(msg.endpoint, 'timers', []);
5363
+
5364
+ if (timeout !== 0) {
5365
+ const timer = setTimeout(() => {
5366
+ publish({occupancy: false});
5367
+ }, timeout * 1000);
5368
+
5369
+ globalStore.getValue(msg.endpoint, 'timers').push(timer);
5370
+ }
5371
+
5372
+ return {occupancy: true};
5373
+ },
5374
+ },
5313
5375
  xiaomi_WXKG01LM_action: {
5314
5376
  cluster: 'genOnOff',
5315
5377
  type: ['attributeReport', 'readResponse'],
@@ -7476,19 +7538,25 @@ const converters = {
7476
7538
  cluster: 'manuSpecificTuya',
7477
7539
  type: ['commandGetData', 'commandDataResponse', 'raw'],
7478
7540
  convert: (model, msg, publish, options, meta) => {
7479
- const dp = msg.data.dp;
7480
- const value = tuya.getDataValue(msg.data.datatype, msg.data.data);
7481
- switch (dp) {
7482
- case tuya.dataPoints.state:
7483
- return {contact: Boolean(value)};
7484
- case tuya.dataPoints.thitBatteryPercentage:
7485
- return {battery: value};
7486
- case tuya.dataPoints.tuyaVibration:
7487
- return {vibration: Boolean(value)};
7488
- default:
7489
- meta.logger.warn(`zigbee-herdsman-converters:tuya_smart_vibration_sensor: NOT RECOGNIZED ` +
7490
- `DP #${dp} with data ${JSON.stringify(msg.data)}`);
7541
+ const result = {};
7542
+ for (const dpValue of msg.data.dpValues) {
7543
+ const value = tuya.getDataValue(dpValue);
7544
+ switch (dpValue.dp) {
7545
+ case tuya.dataPoints.state:
7546
+ result.contact = Boolean(value);
7547
+ break;
7548
+ case tuya.dataPoints.thitBatteryPercentage:
7549
+ result.battery = value;
7550
+ break;
7551
+ case tuya.dataPoints.tuyaVibration:
7552
+ result.vibration = Boolean(value);
7553
+ break;
7554
+ default:
7555
+ meta.logger.warn(`zigbee-herdsman-converters:tuya_smart_vibration_sensor: NOT RECOGNIZED ` +
7556
+ `DP #${dpValue.dp} with data ${JSON.stringify(dpValue)}`);
7557
+ }
7491
7558
  }
7559
+ return result;
7492
7560
  },
7493
7561
  },
7494
7562
  moes_thermostat_tv: {
@@ -7770,6 +7838,40 @@ const converters = {
7770
7838
  };
7771
7839
  },
7772
7840
  },
7841
+ tuya_light_wz5: {
7842
+ cluster: 'manuSpecificTuya',
7843
+ type: ['commandDataResponse', 'commandDataReport'],
7844
+ convert: (model, msg, publish, options, meta) => {
7845
+ const separateWhite = (model.meta && model.meta.separateWhite);
7846
+ const result = {};
7847
+ // eslint-disable-next-line no-unused-vars
7848
+ for (const [i, dpValue] of msg.data.dpValues.entries()) {
7849
+ const dp = dpValue.dp;
7850
+ const value = tuya.getDataValue(dpValue);
7851
+ if (dp === tuya.dataPoints.state) {
7852
+ result.state = value ? 'ON': 'OFF';
7853
+ } else if (dp === tuya.dataPoints.silvercrestSetBrightness) {
7854
+ const brightness = mapNumberRange(value, 0, 1000, 0, 255);
7855
+ if (separateWhite) {
7856
+ result.white_brightness = brightness;
7857
+ } else {
7858
+ result.brightness = brightness;
7859
+ }
7860
+ } else if (dp === tuya.dataPoints.silvercrestSetColor) {
7861
+ const h = parseInt(value.substring(0, 4), 16);
7862
+ const s = parseInt(value.substring(4, 8), 16);
7863
+ const b = parseInt(value.substring(8, 12), 16);
7864
+ result.color_mode = 'hs';
7865
+ result.color = {hue: h, saturation: mapNumberRange(s, 0, 1000, 0, 100)};
7866
+ result.brightness = mapNumberRange(b, 0, 1000, 0, 255);
7867
+ } else if (dp === tuya.dataPoints.silvercrestSetColorTemp) {
7868
+ const [colorTempMin, colorTempMax] = [250, 454];
7869
+ result.color_temp = mapNumberRange(value, 0, 1000, colorTempMax, colorTempMin);
7870
+ }
7871
+ }
7872
+ return result;
7873
+ },
7874
+ },
7773
7875
  // #endregion
7774
7876
 
7775
7877
  // #region Ignore converters (these message dont need parsing).
@@ -1915,6 +1915,8 @@ const converters = {
1915
1915
  }
1916
1916
  }
1917
1917
  }
1918
+
1919
+ return {state: {hue_power_on_behavior: value}};
1918
1920
  },
1919
1921
  },
1920
1922
  hue_power_on_error: {
@@ -2076,7 +2078,7 @@ const converters = {
2076
2078
  await entity.read('aqaraOpple', [0x0000], manufacturerOptions.xiaomi);
2077
2079
  },
2078
2080
  },
2079
- RTCGQ13LM_detection_interval: {
2081
+ aqara_detection_interval: {
2080
2082
  key: ['detection_interval'],
2081
2083
  convertSet: async (entity, key, value, meta) => {
2082
2084
  value *= 1;
@@ -6404,6 +6406,133 @@ const converters = {
6404
6406
  return {state: {click_mode: value}};
6405
6407
  },
6406
6408
  },
6409
+ tuya_light_wz5: {
6410
+ key: ['color', 'color_temp', 'brightness', 'white_brightness'],
6411
+ convertSet: async (entity, key, value, meta) => {
6412
+ const separateWhite = (meta.mapped.meta && meta.mapped.meta.separateWhite);
6413
+ if (key == 'white_brightness' || (!separateWhite && (key == 'brightness'))) {
6414
+ // upscale to 1000
6415
+ let newValue;
6416
+ if (value >= 0 && value <= 255) {
6417
+ newValue = utils.mapNumberRange(value, 0, 255, 0, 1000);
6418
+ } else {
6419
+ throw new Error('Dimmer brightness is out of range 0..255');
6420
+ }
6421
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.silvercrestChangeMode, tuya.silvercrestModes.white);
6422
+ await tuya.sendDataPointValue(entity, tuya.dataPoints.dimmerLevel, newValue);
6423
+
6424
+ return {state: {white_brightness: value}};
6425
+ } else if (key == 'color_temp') {
6426
+ const [colorTempMin, colorTempMax] = [250, 454];
6427
+ const preset = {
6428
+ 'warmest': colorTempMax,
6429
+ 'warm': 454,
6430
+ 'neutral': 370,
6431
+ 'cool': 250,
6432
+ 'coolest': colorTempMin,
6433
+ };
6434
+ if (typeof value === 'string' && isNaN(value)) {
6435
+ const presetName = value.toLowerCase();
6436
+ if (presetName in preset) {
6437
+ value = preset[presetName];
6438
+ } else {
6439
+ throw new Error(`Unknown preset '${value}'`);
6440
+ }
6441
+ } else {
6442
+ value = light.clampColorTemp(Number(value), colorTempMin, colorTempMax, meta.logger);
6443
+ }
6444
+ const data = utils.mapNumberRange(value, colorTempMax, colorTempMin, 0, 1000);
6445
+
6446
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.silvercrestChangeMode, tuya.silvercrestModes.white);
6447
+ await tuya.sendDataPointValue(entity, tuya.dataPoints.silvercrestSetColorTemp, data);
6448
+
6449
+ return {state: {color_temp: value}};
6450
+ } else if (key == 'color' || (separateWhite && (key == 'brightness'))) {
6451
+ const newState = {};
6452
+ if (key == 'brightness') {
6453
+ newState.brightness = value;
6454
+ } else if (key == 'color') {
6455
+ newState.color = value;
6456
+ newState.color_mode = 'hs';
6457
+ }
6458
+
6459
+ const make4sizedString = (v) => {
6460
+ if (v.length >= 4) {
6461
+ return v;
6462
+ } else if (v.length === 3) {
6463
+ return '0' + v;
6464
+ } else if (v.length === 2) {
6465
+ return '00' + v;
6466
+ } else if (v.length === 1) {
6467
+ return '000' + v;
6468
+ } else {
6469
+ return '0000';
6470
+ }
6471
+ };
6472
+
6473
+ const fillInHSB = (h, s, b, state) => {
6474
+ // Define default values. Device expects leading zero in string.
6475
+ const hsb = {
6476
+ h: '0168', // 360
6477
+ s: '03e8', // 1000
6478
+ b: '03e8', // 1000
6479
+ };
6480
+
6481
+ if (h) {
6482
+ // The device expects 0-359
6483
+ if (h >= 360) {
6484
+ h = 359;
6485
+ }
6486
+ hsb.h = make4sizedString(h.toString(16));
6487
+ } else if (state.color && state.color.hue) {
6488
+ hsb.h = make4sizedString(state.color.hue.toString(16));
6489
+ }
6490
+
6491
+ // Device expects 0-1000, saturation normally is 0-100 so we expect that from the user
6492
+ // The device expects a round number, otherwise everything breaks
6493
+ if (s) {
6494
+ hsb.s = make4sizedString(utils.mapNumberRange(s, 0, 100, 0, 1000).toString(16));
6495
+ } else if (state.color && state.color.saturation) {
6496
+ hsb.s = make4sizedString(utils.mapNumberRange(state.color.saturation, 0, 100, 0, 1000).toString(16));
6497
+ }
6498
+
6499
+ // Scale 0-255 to 0-1000 what the device expects.
6500
+ if (b) {
6501
+ hsb.b = make4sizedString(utils.mapNumberRange(b, 0, 255, 0, 1000).toString(16));
6502
+ } else if (state.brightness) {
6503
+ hsb.b = make4sizedString(utils.mapNumberRange(state.brightness, 0, 255, 0, 1000).toString(16));
6504
+ }
6505
+
6506
+ return hsb;
6507
+ };
6508
+
6509
+ const hsb = fillInHSB(
6510
+ value.h || value.hue || null,
6511
+ value.s || value.saturation || null,
6512
+ value.b || value.brightness || (key == 'brightness') ? value : null,
6513
+ meta.state,
6514
+ );
6515
+
6516
+
6517
+ let data = [];
6518
+ data = data.concat(tuya.convertStringToHexArray(hsb.h));
6519
+ data = data.concat(tuya.convertStringToHexArray(hsb.s));
6520
+ data = data.concat(tuya.convertStringToHexArray(hsb.b));
6521
+
6522
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.silvercrestChangeMode, tuya.silvercrestModes.color);
6523
+ await tuya.sendDataPointStringBuffer(entity, tuya.dataPoints.silvercrestSetColor, data);
6524
+
6525
+ if (separateWhite && meta.state.white_brightness != undefined) {
6526
+ // restore white state
6527
+ const newValue = utils.mapNumberRange(meta.state.white_brightness, 0, 255, 0, 1000);
6528
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.silvercrestChangeMode, tuya.silvercrestModes.white);
6529
+ await tuya.sendDataPointValue(entity, tuya.dataPoints.dimmerLevel, newValue);
6530
+ }
6531
+
6532
+ return {state: newState};
6533
+ }
6534
+ },
6535
+ },
6407
6536
  // #endregion
6408
6537
 
6409
6538
  // #region Ignore converters
package/devices/awox.js CHANGED
@@ -10,6 +10,11 @@ module.exports = [
10
10
  },
11
11
  {
12
12
  fingerprint: [
13
+ {type: 'Router', manufacturerName: 'AwoX', modelID: 'TLSR82xx', endpoints: [
14
+ {ID: 1, profileID: 260, deviceID: 269, inputClusters: [0, 3, 4, 5, 6, 8, 768, 4096, 64599, 10], outputClusters: [6]},
15
+ {ID: 3, profileID: 4751, deviceID: 269, inputClusters: [65360, 65361], outputClusters: [65360, 65361]},
16
+ {ID: 242, profileID: 41440, deviceID: 97, inputClusters: [], outputClusters: [33]},
17
+ ]},
13
18
  {type: 'Router', manufacturerName: 'AwoX', modelID: 'TLSR82xx', endpoints: [
14
19
  {ID: 1, profileID: 260, deviceID: 258, inputClusters: [0, 3, 4, 5, 6, 8, 768, 4096], outputClusters: [6, 25]},
15
20
  {ID: 3, profileID: 49152, deviceID: 258, inputClusters: [65360, 65361], outputClusters: [65360, 65361]},
@@ -70,7 +70,7 @@ module.exports = [
70
70
  {
71
71
  zigbeeModel: ['Bticino Din power consumption module '],
72
72
  model: 'F20T60A',
73
- description: 'DIN power consumption module',
73
+ description: 'DIN power consumption module (same as Legrand 412015)',
74
74
  vendor: 'BTicino',
75
75
  extend: extend.switch(),
76
76
  fromZigbee: [fz.identify, fz.on_off, fz.electrical_measurement, fz.legrand_device_mode, fz.ignore_basic_report, fz.ignore_genOta],
@@ -196,12 +196,15 @@ module.exports = [
196
196
  tz.thermostat_occupied_heating_setpoint, tz.thermostat_occupied_cooling_setpoint,
197
197
  tz.thermostat_setpoint_raise_lower, tz.thermostat_remote_sensing,
198
198
  tz.thermostat_control_sequence_of_operation, tz.thermostat_system_mode,
199
- tz.thermostat_relay_status_log, tz.fan_mode, tz.thermostat_running_state],
200
- exposes: [e.battery(), exposes.climate().withSetpoint('occupied_heating_setpoint', 10, 30, 1).withLocalTemperature()
201
- .withSystemMode(['off', 'heat', 'cool', 'emergency_heating'])
202
- .withRunningState(['idle', 'heat', 'cool', 'fan_only']).withFanMode(['auto', 'on'])
203
- .withSetpoint('occupied_cooling_setpoint', 10, 30, 1)
204
- .withLocalTemperatureCalibration(-30, 30, 0.1)],
199
+ tz.thermostat_relay_status_log, tz.fan_mode, tz.thermostat_running_state, tz.thermostat_temperature_setpoint_hold],
200
+ exposes: [e.battery(),
201
+ exposes.binary('temperature_setpoint_hold', ea.ALL, true, false)
202
+ .withDescription('Prevent changes. `false` = run normally. `true` = prevent from making changes.'),
203
+ exposes.climate().withSetpoint('occupied_heating_setpoint', 10, 30, 1).withLocalTemperature()
204
+ .withSystemMode(['off', 'heat', 'cool', 'emergency_heating'])
205
+ .withRunningState(['idle', 'heat', 'cool', 'fan_only']).withFanMode(['auto', 'on'])
206
+ .withSetpoint('occupied_cooling_setpoint', 10, 30, 1)
207
+ .withLocalTemperatureCalibration(-30, 30, 0.1)],
205
208
  meta: {battery: {voltageToPercentage: '3V_1500_2800'}},
206
209
  configure: async (device, coordinatorEndpoint, logger) => {
207
210
  const endpoint = device.getEndpoint(1);
@@ -210,6 +213,7 @@ module.exports = [
210
213
  await reporting.thermostatRunningState(endpoint);
211
214
  await reporting.thermostatTemperature(endpoint);
212
215
  await reporting.fanMode(endpoint);
216
+ await reporting.thermostatTemperatureSetpointHold(endpoint);
213
217
  },
214
218
  },
215
219
  {
package/devices/innr.js CHANGED
@@ -79,6 +79,14 @@ module.exports = [
79
79
  extend: extend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 555], supportsHS: true}),
80
80
  meta: {enhancedHue: false, applyRedFix: true, turnsOffAtBrightness1: true},
81
81
  },
82
+ {
83
+ zigbeeModel: ['RB 251 C'],
84
+ model: 'RB 251 C',
85
+ vendor: 'Innr',
86
+ description: 'E14 bulb RGBW',
87
+ extend: extend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 555], supportsHS: true}),
88
+ meta: {enhancedHue: false, applyRedFix: true, turnsOffAtBrightness1: true},
89
+ },
82
90
  {
83
91
  zigbeeModel: ['RB 265'],
84
92
  model: 'RB 265',
package/devices/iris.js CHANGED
@@ -3,6 +3,7 @@ const fz = {...require('../converters/fromZigbee'), legacy: require('../lib/lega
3
3
  const tz = require('../converters/toZigbee');
4
4
  const reporting = require('../lib/reporting');
5
5
  const e = exposes.presets;
6
+ const extend = require('../lib/extend');
6
7
 
7
8
  module.exports = [
8
9
  {
@@ -18,6 +19,8 @@ module.exports = [
18
19
  await reporting.bind(endpoint, coordinatorEndpoint, ['msTemperatureMeasurement', 'genPowerCfg']);
19
20
  await reporting.temperature(endpoint);
20
21
  await reporting.batteryVoltage(endpoint);
22
+ device.powerSource = 'Battery';
23
+ device.save();
21
24
  },
22
25
  exposes: [e.contact(), e.battery_low(), e.tamper(), e.temperature(), e.battery()],
23
26
  },
@@ -144,4 +147,18 @@ module.exports = [
144
147
  },
145
148
  exposes: [e.switch(), e.battery()],
146
149
  },
150
+ {
151
+ zigbeeModel: ['1113-S'],
152
+ model: 'iL03_1',
153
+ vendor: 'Iris',
154
+ description: 'Smart plug',
155
+ extend: extend.switch(),
156
+ configure: async (device, coordinatorEndpoint, logger) => {
157
+ const endpoint = device.getEndpoint(1);
158
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
159
+ await reporting.onOff(endpoint);
160
+ device.powerSource = 'Mains (single phase)';
161
+ device.save();
162
+ },
163
+ },
147
164
  ];
@@ -19,7 +19,7 @@ module.exports = [
19
19
  zigbeeModel: [' Contactor\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000'+
20
20
  '\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000'],
21
21
  model: 'FC80CC',
22
- description: 'Legrand (or Bticino) DIN contactor module (note: Legrand 412171 may be similar to Bticino FC80CC)',
22
+ description: 'Legrand (or Bticino) DIN contactor module',
23
23
  vendor: 'Legrand',
24
24
  extend: extend.switch(),
25
25
  fromZigbee: [fz.identify, fz.on_off, fz.electrical_measurement, fz.legrand_device_mode, fz.ignore_basic_report, fz.ignore_genOta],
package/devices/lidl.js CHANGED
@@ -667,7 +667,7 @@ module.exports = [
667
667
  fingerprint: [{modelID: 'TS0101', manufacturerName: '_TZ3000_pnzfdr9y'}],
668
668
  model: 'HG06619',
669
669
  vendor: 'Lidl',
670
- description: 'Silvercrest oudoor plug',
670
+ description: 'Silvercrest outdoor plug',
671
671
  extend: extend.switch(),
672
672
  configure: async (device, coordinatorEndpoint, logger) => {
673
673
  const endpoint = device.getEndpoint(1);
package/devices/nous.js CHANGED
@@ -7,6 +7,15 @@ const e = exposes.presets;
7
7
  const ea = exposes.access;
8
8
 
9
9
  module.exports = [
10
+ {
11
+ fingerprint: [{modelID: 'TS0201', manufacturerName: '_TZ3000_lbtpiody'}],
12
+ model: 'E5',
13
+ vendor: 'Nous',
14
+ description: 'Temperature & humidity',
15
+ fromZigbee: [fz.temperature, fz.humidity, fz.battery],
16
+ toZigbee: [],
17
+ exposes: [e.temperature(), e.humidity(), e.battery()],
18
+ },
10
19
  {
11
20
  fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_nnrfa68v'}],
12
21
  model: 'E6',
package/devices/osram.js CHANGED
@@ -390,14 +390,15 @@ module.exports = [
390
390
  ota: ota.ledvance,
391
391
  },
392
392
  {
393
- zigbeeModel: ['Zigbee 3.0 DALI CONV LI'],
393
+ zigbeeModel: ['Zigbee 3.0 DALI CONV LI', 'Zigbee 3.0 DALI CONV LI\u0000'],
394
394
  model: '4062172044776_1',
395
395
  vendor: 'OSRAM',
396
396
  description: 'Zigbee 3.0 DALI CONV LI dimmer for DALI-based luminaires (only one device)',
397
397
  extend: extend.ledvance.light_onoff_brightness(),
398
398
  },
399
399
  {
400
- fingerprint: [{modelID: 'Zigbee 3.0 DALI CONV LI', endpoints: [{ID: 10}, {ID: 25}, {ID: 242}]}],
400
+ fingerprint: [{modelID: 'Zigbee 3.0 DALI CONV LI', endpoints: [{ID: 10}, {ID: 25}, {ID: 242}]},
401
+ {modelID: 'Zigbee 3.0 DALI CONV LI\u0000', endpoints: [{ID: 10}, {ID: 25}, {ID: 242}]}],
401
402
  model: '4062172044776_2',
402
403
  vendor: 'OSRAM',
403
404
  description: 'Zigbee 3.0 DALI CONV LI dimmer for DALI-based luminaires (one device and pushbutton)',
@@ -410,7 +411,8 @@ module.exports = [
410
411
  },
411
412
  },
412
413
  {
413
- fingerprint: [{modelID: 'Zigbee 3.0 DALI CONV LI', endpoints: [{ID: 10}, {ID: 11}, {ID: 242}]}],
414
+ fingerprint: [{modelID: 'Zigbee 3.0 DALI CONV LI', endpoints: [{ID: 10}, {ID: 11}, {ID: 242}]},
415
+ {modelID: 'Zigbee 3.0 DALI CONV LI\u0000', endpoints: [{ID: 10}, {ID: 11}, {ID: 242}]}],
414
416
  model: '4062172044776_3',
415
417
  vendor: 'OSRAM',
416
418
  description: 'Zigbee 3.0 DALI CONV LI dimmer for DALI-based luminaires (with two devices)',
@@ -430,7 +432,8 @@ module.exports = [
430
432
  },
431
433
  },
432
434
  {
433
- fingerprint: [{modelID: 'Zigbee 3.0 DALI CONV LI', endpoints: [{ID: 10}, {ID: 11}, {ID: 25}, {ID: 242}]}],
435
+ fingerprint: [{modelID: 'Zigbee 3.0 DALI CONV LI', endpoints: [{ID: 10}, {ID: 11}, {ID: 25}, {ID: 242}]},
436
+ {modelID: 'Zigbee 3.0 DALI CONV LI\u0000', endpoints: [{ID: 10}, {ID: 11}, {ID: 25}, {ID: 242}]}],
434
437
  model: '4062172044776_4',
435
438
  vendor: 'OSRAM',
436
439
  description: 'Zigbee 3.0 DALI CONV LI dimmer for DALI-based luminaires (with two devices and pushbutton)',
@@ -29,6 +29,15 @@ const hueExtend = {
29
29
  };
30
30
 
31
31
  module.exports = [
32
+ {
33
+ zigbeeModel: ['929003047501'],
34
+ model: '929003047501',
35
+ vendor: 'Philips',
36
+ description: 'Centura recessed spotlight',
37
+ meta: {turnsOffAtBrightness1: true},
38
+ extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
39
+ ota: ota.zigbeeOTA,
40
+ },
32
41
  {
33
42
  zigbeeModel: ['915005996401'],
34
43
  model: '915005996401',
@@ -0,0 +1,74 @@
1
+ const exposes = require('../lib/exposes');
2
+ const fz = {...require('../converters/fromZigbee'), legacy: require('../lib/legacy').fromZigbee};
3
+ const tz = require('../converters/toZigbee');
4
+ const ea = exposes.access;
5
+
6
+ module.exports = [
7
+ {
8
+ fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_6qoazbre'}],
9
+ model: 'WZ5_dim',
10
+ vendor: 'Skydance',
11
+ description: 'Zigbee & RF 5 in 1 LED controller (DIM mode)',
12
+ fromZigbee: [fz.tuya_light_wz5],
13
+ toZigbee: [tz.tuya_dimmer_state, tz.tuya_light_wz5],
14
+ exposes: [
15
+ exposes.light().withBrightness().setAccess('state',
16
+ ea.STATE_SET).setAccess('brightness', ea.STATE_SET),
17
+ ],
18
+ },
19
+ {
20
+ fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_gz3n0tzf'}],
21
+ model: 'WZ5_cct',
22
+ vendor: 'Skydance',
23
+ description: 'Zigbee & RF 5 in 1 LED controller (CCT mode)',
24
+ fromZigbee: [fz.tuya_light_wz5],
25
+ toZigbee: [tz.tuya_dimmer_state, tz.tuya_light_wz5],
26
+ exposes: [
27
+ exposes.light().withBrightness().setAccess('state',
28
+ ea.STATE_SET).setAccess('brightness', ea.STATE_SET).withColorTemp([250, 454]).setAccess('color_temp', ea.STATE_SET),
29
+ ],
30
+ },
31
+ {
32
+ fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_9hghastn'}],
33
+ model: 'WZ5_rgb',
34
+ vendor: 'Skydance',
35
+ description: 'Zigbee & RF 5 in 1 LED controller (RGB mode)',
36
+ fromZigbee: [fz.tuya_light_wz5],
37
+ toZigbee: [tz.tuya_dimmer_state, tz.tuya_light_wz5],
38
+ exposes: [
39
+ exposes.light().withBrightness().setAccess('state', ea.STATE_SET).setAccess('brightness',
40
+ ea.STATE_SET).withColor('hs'),
41
+ ],
42
+ },
43
+ {
44
+ fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_3thxjahu'}],
45
+ model: 'WZ5_rgbw',
46
+ vendor: 'Skydance',
47
+ description: 'Zigbee & RF 5 in 1 LED controller (RGBW mode)',
48
+ fromZigbee: [fz.tuya_light_wz5],
49
+ toZigbee: [tz.tuya_dimmer_state, tz.tuya_light_wz5],
50
+ exposes: [
51
+ exposes.light().withBrightness().setAccess('state', ea.STATE_SET).setAccess('brightness',
52
+ ea.STATE_SET).withColor('hs'),
53
+ exposes.numeric('white_brightness', ea.STATE_SET).withValueMin(0).withValueMax(254).withDescription(
54
+ 'White brightness of this light'),
55
+ ],
56
+ meta: {separateWhite: true},
57
+ },
58
+ {
59
+ fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_mde0utnv'}],
60
+ model: 'WZ5_rgbcct',
61
+ vendor: 'Skydance',
62
+ description: 'Zigbee & RF 5 in 1 LED controller (RGB+CCT mode)',
63
+ fromZigbee: [fz.tuya_light_wz5],
64
+ toZigbee: [tz.tuya_dimmer_state, tz.tuya_light_wz5],
65
+ exposes: [
66
+ exposes.light().withBrightness().setAccess('state', ea.STATE_SET).setAccess('brightness',
67
+ ea.STATE_SET).withColor('hs').withColorTemp([250, 454]).setAccess('color_temp',
68
+ ea.STATE_SET),
69
+ exposes.numeric('white_brightness', ea.STATE_SET).withValueMin(0).withValueMax(254).withDescription(
70
+ 'White brightness of this light'),
71
+ ],
72
+ meta: {separateWhite: true},
73
+ },
74
+ ];
package/devices/tuya.js CHANGED
@@ -12,7 +12,7 @@ const utils = require('../lib/utils');
12
12
 
13
13
  const TS011Fplugs = ['_TZ3000_5f43h46b', '_TZ3000_cphmq0q7', '_TZ3000_dpo1ysak', '_TZ3000_ew3ldmgx', '_TZ3000_gjnozsaz',
14
14
  '_TZ3000_jvzvulen', '_TZ3000_mraovvmm', '_TZ3000_nfnmi125', '_TZ3000_ps3dmato', '_TZ3000_w0qqde0g', '_TZ3000_u5u4cakc',
15
- '_TZ3000_rdtixbnu', '_TZ3000_typdpbpg', '_TZ3000_v1pdxuqq'];
15
+ '_TZ3000_rdtixbnu', '_TZ3000_typdpbpg', '_TZ3000_v1pdxuqq', '_TZ3000_bfn1w0mm'];
16
16
 
17
17
  const tzLocal = {
18
18
  TS0504B_color: {
@@ -1142,7 +1142,9 @@ module.exports = [
1142
1142
  toZigbee: [tz.on_off, tz.tuya_switch_power_outage_memory],
1143
1143
  configure: async (device, coordinatorEndpoint, logger) => {
1144
1144
  const endpoint = device.getEndpoint(1);
1145
- await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
1145
+ // Enables reporting of physical state changes
1146
+ // https://github.com/Koenkk/zigbee2mqtt/issues/9057#issuecomment-1007742130
1147
+ await endpoint.read('genBasic', ['manufacturerName', 'zclVersion', 'appVersion', 'modelId', 'powerSource', 0xfffe]);
1146
1148
  endpoint.saveClusterAttributeKeyValue('haElectricalMeasurement', {acCurrentDivisor: 1000, acCurrentMultiplier: 1});
1147
1149
  endpoint.saveClusterAttributeKeyValue('seMetering', {divisor: 100, multiplier: 1});
1148
1150
  device.save();
package/devices/xiaomi.js CHANGED
@@ -686,6 +686,11 @@ module.exports = [
686
686
  exposes.enum('operation_mode', ea.ALL, ['control_relay', 'decoupled'])
687
687
  .withDescription('Decoupled mode'),
688
688
  ],
689
+ configure: async (device, coordinatorEndpoint, logger) => {
690
+ device.type = 'Router';
691
+ device.powerSource = 'Mains (single phase)';
692
+ device.save();
693
+ },
689
694
  },
690
695
  {
691
696
  zigbeeModel: ['lumi.switch.b2nacn02'],
@@ -868,19 +873,17 @@ module.exports = [
868
873
  zigbeeModel: ['lumi.motion.agl02'],
869
874
  model: 'RTCGQ12LM',
870
875
  vendor: 'Xiaomi',
871
- description: 'Aqara T1 human body movement and illuminance sensor (illuminance not supported for now)',
872
- fromZigbee: [fz.occupancy, fz.occupancy_timeout, fz.battery],
873
- toZigbee: [tz.occupancy_timeout],
874
- exposes: [e.occupancy(), e.battery(),
875
- exposes.numeric('occupancy_timeout', exposes.access.ALL).withValueMin(0).withValueMax(65535).withUnit('s')
876
- .withDescription('Time in seconds till occupancy goes to false')],
876
+ description: 'Aqara T1 human body movement and illuminance sensor',
877
+ fromZigbee: [fz.RTCGQ12LM_occupancy_illuminance, fz.aqara_opple, fz.battery],
878
+ toZigbee: [tz.aqara_detection_interval],
879
+ exposes: [e.occupancy(), e.illuminance().withUnit('lx').withDescription('Measured illuminance in lux'),
880
+ exposes.numeric('detection_interval', ea.ALL).withValueMin(2).withValueMax(65535).withUnit('s')
881
+ .withDescription('Time interval for detecting actions'), e.battery()],
877
882
  meta: {battery: {voltageToPercentage: '3V_2100'}},
878
883
  configure: async (device, coordinatorEndpoint, logger) => {
879
884
  const endpoint = device.getEndpoint(1);
880
- await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'msOccupancySensing']);
881
- await reporting.occupancy(endpoint);
882
- await reporting.batteryVoltage(endpoint);
883
- await endpoint.read('msOccupancySensing', ['pirOToUDelay']);
885
+ await endpoint.read('genPowerCfg', ['batteryVoltage']);
886
+ await endpoint.read('aqaraOpple', [0x0102], {manufacturerCode: 0x115f});
884
887
  },
885
888
  },
886
889
  {
@@ -888,15 +891,15 @@ module.exports = [
888
891
  model: 'RTCGQ13LM',
889
892
  vendor: 'Xiaomi',
890
893
  description: 'Aqara high precision motion sensor',
891
- fromZigbee: [fz.occupancy_with_timeout, fz.aqara_opple, fz.battery],
892
- toZigbee: [tz.RTCGQ13LM_detection_interval, tz.RTCGQ13LM_motion_sensitivity],
893
- exposes: [e.occupancy(), e.battery(),
894
- exposes.enum('motion_sensitivity', exposes.access.ALL, ['low', 'medium', 'high']),
895
- exposes.numeric('detection_interval', exposes.access.ALL).withValueMin(2).withValueMax(180).withUnit('s')
896
- .withDescription('Time interval for detecting actions')],
894
+ fromZigbee: [fz.RTCGQ13LM_occupancy, fz.aqara_opple, fz.battery],
895
+ toZigbee: [tz.aqara_detection_interval, tz.RTCGQ13LM_motion_sensitivity],
896
+ exposes: [e.occupancy(), exposes.enum('motion_sensitivity', ea.ALL, ['low', 'medium', 'high']),
897
+ exposes.numeric('detection_interval', ea.ALL).withValueMin(2).withValueMax(65535).withUnit('s')
898
+ .withDescription('Time interval for detecting actions'), e.battery()],
897
899
  meta: {battery: {voltageToPercentage: '3V_2100'}},
898
900
  configure: async (device, coordinatorEndpoint, logger) => {
899
901
  const endpoint = device.getEndpoint(1);
902
+ await endpoint.read('genPowerCfg', ['batteryVoltage']);
900
903
  await endpoint.read('aqaraOpple', [0x0102], {manufacturerCode: 0x115f});
901
904
  await endpoint.read('aqaraOpple', [0x010c], {manufacturerCode: 0x115f});
902
905
  },
@@ -9,14 +9,18 @@ const tuya = require('../lib/tuya');
9
9
  const fzLocal = {
10
10
  ZMRM02: {
11
11
  cluster: 'manuSpecificTuya',
12
- type: ['commandGetData', 'commandSetDataResponse'],
12
+ type: ['commandGetData', 'commandSetDataResponse', 'commandDataResponse'],
13
13
  convert: (model, msg, publish, options, meta) => {
14
14
  const dpValue = tuya.firstDpValue(msg, meta, 'ZMRM02');
15
- const button = dpValue.dp;
16
- const actionValue = tuya.getDataValue(dpValue);
17
- const lookup = {0: 'single', 1: 'double', 2: 'hold'};
18
- const action = lookup[actionValue];
19
- return {action: `button_${button}_${action}`};
15
+ if (dpValue.dp === 10) {
16
+ return {battery: tuya.getDataValue(dpValue)};
17
+ } else {
18
+ const button = dpValue.dp;
19
+ const actionValue = tuya.getDataValue(dpValue);
20
+ const lookup = {0: 'single', 1: 'double', 2: 'hold'};
21
+ const action = lookup[actionValue];
22
+ return {action: `button_${button}_${action}`};
23
+ }
20
24
  },
21
25
  },
22
26
  };
@@ -90,7 +94,7 @@ module.exports = [
90
94
  fromZigbee: [fzLocal.ZMRM02],
91
95
  toZigbee: [],
92
96
  onEvent: tuya.onEventSetTime,
93
- exposes: [e.action([
97
+ exposes: [e.battery(), e.action([
94
98
  'button_1_hold', 'button_1_single', 'button_1_double',
95
99
  'button_2_hold', 'button_2_single', 'button_2_double',
96
100
  'button_3_hold', 'button_3_single', 'button_3_double',
package/lib/exposes.js CHANGED
@@ -527,7 +527,7 @@ module.exports = {
527
527
  current: () => new Numeric('current', access.STATE).withUnit('A').withDescription('Instantaneous measured electrical current'),
528
528
  current_phase_b: () => new Numeric('current_phase_b', access.STATE).withUnit('A').withDescription('Instantaneous measured electrical current on phase B'),
529
529
  current_phase_c: () => new Numeric('current_phase_c', access.STATE).withUnit('A').withDescription('Instantaneous measured electrical current on phase C'),
530
- deadzone_temperature: () => new Numeric('deadzone_temperature', access.STATE_SET).withUnit('°C').withDescription('The delta between local_temperature and current_heating_setpoint to trigger Heat').withValueMin(0.1).withValueMax(5),
530
+ deadzone_temperature: () => new Numeric('deadzone_temperature', access.STATE_SET).withUnit('°C').withDescription('The delta between local_temperature and current_heating_setpoint to trigger Heat').withValueMin(0).withValueMax(5).withValueStep(1),
531
531
  device_temperature: () => new Numeric('device_temperature', access.STATE).withUnit('°C').withDescription('Temperature of the device'),
532
532
  eco2: () => new Numeric('eco2', access.STATE).withUnit('ppm').withDescription('Measured eCO2 value'),
533
533
  eco_mode: () => new Binary('eco_mode', access.STATE_SET, 'ON', 'OFF').withDescription('ECO mode (energy saving mode)'),
package/lib/tuya.js CHANGED
@@ -85,10 +85,6 @@ async function onEventMeasurementPoll(type, data, device, options) {
85
85
  const interval = setInterval(async () => {
86
86
  try {
87
87
  await endpoint.read('haElectricalMeasurement', ['rmsVoltage', 'rmsCurrent', 'activePower']);
88
- await endpoint.read('seMetering', ['currentSummDelivered']);
89
- if (device.manufacturerName === '_TZ3000_u5u4cakc') {
90
- await endpoint.read('genOnOff', ['onOff']);
91
- }
92
88
  } catch (error) {/* Do nothing*/}
93
89
  }, seconds*1000);
94
90
  globalStore.putValue(device, 'interval', interval);
@@ -384,6 +380,7 @@ const dataPoints = {
384
380
  // Silvercrest
385
381
  silvercrestChangeMode: 2,
386
382
  silvercrestSetBrightness: 3,
383
+ silvercrestSetColorTemp: 4,
387
384
  silvercrestSetColor: 5,
388
385
  silvercrestSetEffect: 6,
389
386
  // Fantem
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "14.0.371",
3
+ "version": "14.0.375",
4
4
  "lockfileVersion": 1,
5
5
  "requires": true,
6
6
  "dependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "14.0.371",
3
+ "version": "14.0.375",
4
4
  "description": "Collection of device converters to be used with zigbee-herdsman",
5
5
  "main": "index.js",
6
6
  "files": [