zigbee-herdsman-converters 14.0.369 → 14.0.373

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.
@@ -1164,7 +1164,8 @@ const converters = {
1164
1164
 
1165
1165
  if (options.simulated_brightness) {
1166
1166
  globalStore.putValue(msg.endpoint, 'simulated_brightness_brightness', msg.data.level);
1167
- payload.brightness = msg.data.level;
1167
+ const property = postfixWithEndpointName('brightness', msg, model);
1168
+ payload[property] = msg.data.level;
1168
1169
  }
1169
1170
 
1170
1171
  return payload;
@@ -1195,7 +1196,8 @@ const converters = {
1195
1196
  brightness += delta;
1196
1197
  brightness = numberWithinRange(brightness, 0, 255);
1197
1198
  globalStore.putValue(msg.endpoint, 'simulated_brightness_brightness', brightness);
1198
- publish({brightness});
1199
+ const property = postfixWithEndpointName('brightness', msg, model);
1200
+ publish({[property]: brightness});
1199
1201
  }, intervalOpts);
1200
1202
 
1201
1203
  globalStore.putValue(msg.endpoint, 'simulated_brightness_timer', timer);
@@ -1225,7 +1227,8 @@ const converters = {
1225
1227
  brightness += delta;
1226
1228
  brightness = numberWithinRange(brightness, 0, 255);
1227
1229
  globalStore.putValue(msg.endpoint, 'simulated_brightness_brightness', brightness);
1228
- payload.brightness = brightness;
1230
+ const property = postfixWithEndpointName('brightness', msg, model);
1231
+ payload[property] = brightness;
1229
1232
  }
1230
1233
 
1231
1234
  return payload;
@@ -2574,6 +2577,28 @@ const converters = {
2574
2577
  }
2575
2578
  },
2576
2579
  },
2580
+ wls100z_water_leak: {
2581
+ cluster: 'manuSpecificTuya',
2582
+ type: ['commandDataResponse', 'commandDataReport'],
2583
+ convert: (model, msg, publish, options, meta) => {
2584
+ const result = {};
2585
+ for (const dpValue of msg.data.dpValues) {
2586
+ const value = tuya.getDataValue(dpValue);
2587
+ switch (dpValue.dp) {
2588
+ case tuya.dataPoints.wlsWaterLeak:
2589
+ result.water_leak = value < 1;
2590
+ break;
2591
+ case tuya.dataPoints.wlsBatteryPercentage:
2592
+ result.battery = value;
2593
+ break;
2594
+ default:
2595
+ meta.logger.warn(`zigbee-herdsman-converters:wls100z_water_leak:` +
2596
+ `NOT RECOGNIZED DP #${dpValue.dp} with data ${JSON.stringify(dpValue)}`);
2597
+ }
2598
+ }
2599
+ return result;
2600
+ },
2601
+ },
2577
2602
  livolo_switch_state: {
2578
2603
  cluster: 'genOnOff',
2579
2604
  type: ['attributeReport', 'readResponse'],
@@ -4679,17 +4704,21 @@ const converters = {
4679
4704
  },
4680
4705
  tuya_smoke: {
4681
4706
  cluster: 'manuSpecificTuya',
4682
- type: ['commandDataResponse'],
4707
+ type: ['commandDataResponse', 'commandDataReport'],
4683
4708
  convert: (model, msg, publish, options, meta) => {
4684
- const dpValue = tuya.firstDpValue(msg, meta, 'tuya_smoke');
4685
- const dp = dpValue.dp;
4686
- const value = tuya.getDataValue(dpValue);
4687
- switch (dp) {
4688
- case tuya.dataPoints.state:
4689
- return {smoke: value === 0};
4690
- default:
4691
- meta.logger.warn(`zigbee-herdsman-converters:tuya_smoke: Unrecognized DP #${ dp} with data ${JSON.stringify(dpValue)}`);
4709
+ const result = {};
4710
+ for (const dpValue of msg.data.dpValues) {
4711
+ const dp = dpValue.dp;
4712
+ const value = tuya.getDataValue(dpValue);
4713
+ switch (dp) {
4714
+ case tuya.dataPoints.state:
4715
+ result.smoke = value === 0;
4716
+ break;
4717
+ default:
4718
+ meta.logger.warn(`zigbee-herdsman-converters:tuya_smoke: Unrecognized DP #${ dp} with data ${JSON.stringify(dpValue)}`);
4719
+ }
4692
4720
  }
4721
+ return result;
4693
4722
  },
4694
4723
  },
4695
4724
  tuya_woox_smoke: {
@@ -4988,7 +5017,8 @@ const converters = {
4988
5017
  aqara_opple: {
4989
5018
  cluster: 'aqaraOpple',
4990
5019
  type: ['attributeReport', 'readResponse'],
4991
- 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')],
4992
5022
  convert: (model, msg, publish, options, meta) => {
4993
5023
  const payload = {};
4994
5024
  if (msg.data.hasOwnProperty('247')) {
@@ -5113,6 +5143,8 @@ const converters = {
5113
5143
  if (['QBKG19LM', 'QBKG20LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM'].includes(model.model)) {
5114
5144
  const mapping = model.model === 'QBCZ15LM' ? 'usb' : 'right';
5115
5145
  payload[`state_${mapping}`] = value === 1 ? 'ON' : 'OFF';
5146
+ } else if (['RTCGQ12LM'].includes(model.model)) {
5147
+ payload.illuminance = calibrateAndPrecisionRoundOptions(value, options, 'illuminance');
5116
5148
  }
5117
5149
  } else if (index === 149) {
5118
5150
  payload.energy = precisionRound(value, 2); // 0x95
@@ -5128,7 +5160,7 @@ const converters = {
5128
5160
  if (msg.data.hasOwnProperty('0')) payload.detection_period = msg.data['0'];
5129
5161
  if (msg.data.hasOwnProperty('4')) payload.mode_switch = {4: 'anti_flicker_mode', 1: 'quick_mode'}[msg.data['4']];
5130
5162
  if (msg.data.hasOwnProperty('10')) payload.switch_type = {1: 'toggle', 2: 'momentary'}[msg.data['10']];
5131
- if (msg.data.hasOwnProperty('258')) payload.detection_interval = msg.data['258'];
5163
+ if (msg.data.hasOwnProperty('258')) payload.occupancy_timeout = msg.data['258'];
5132
5164
  if (msg.data.hasOwnProperty('268')) payload.motion_sensitivity = {1: 'low', 2: 'medium', 3: 'high'}[msg.data['268']];
5133
5165
  if (msg.data.hasOwnProperty('512')) {
5134
5166
  if (['ZNCZ15LM', 'QBCZ14LM', 'QBCZ15LM'].includes(model.model)) {
@@ -5281,6 +5313,65 @@ const converters = {
5281
5313
  return payload;
5282
5314
  },
5283
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('occupancy_timeout') ? meta.state.occupancy_timeout : 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('occupancy_timeout') ? meta.state.occupancy_timeout : 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
+ },
5284
5375
  xiaomi_WXKG01LM_action: {
5285
5376
  cluster: 'genOnOff',
5286
5377
  type: ['attributeReport', 'readResponse'],
@@ -5943,7 +6034,13 @@ const converters = {
5943
6034
  ZMCSW032D_cover_position: {
5944
6035
  cluster: 'closuresWindowCovering',
5945
6036
  type: ['attributeReport', 'readResponse'],
5946
- options: [exposes.options.invert_cover()],
6037
+ options: [
6038
+ exposes.options.invert_cover(),
6039
+ exposes.numeric('time_close')
6040
+ .withDescription(`Set the full closing time of the roller shutter (e.g. set it to 20) (value is in s).`),
6041
+ exposes.numeric('time_open')
6042
+ .withDescription(`Set the full opening time of the roller shutter (e.g. set it to 21) (value is in s).`),
6043
+ ],
5947
6044
  convert: (model, msg, publish, options, meta) => {
5948
6045
  const result = {};
5949
6046
  const timeCoverSetMiddle = 60;
@@ -7437,6 +7534,25 @@ const converters = {
7437
7534
  }
7438
7535
  },
7439
7536
  },
7537
+ tuya_smart_vibration_sensor: {
7538
+ cluster: 'manuSpecificTuya',
7539
+ type: ['commandGetData', 'commandDataResponse', 'raw'],
7540
+ convert: (model, msg, publish, options, meta) => {
7541
+ const dp = msg.data.dp;
7542
+ const value = tuya.getDataValue(msg.data.datatype, msg.data.data);
7543
+ switch (dp) {
7544
+ case tuya.dataPoints.state:
7545
+ return {contact: Boolean(value)};
7546
+ case tuya.dataPoints.thitBatteryPercentage:
7547
+ return {battery: value};
7548
+ case tuya.dataPoints.tuyaVibration:
7549
+ return {vibration: Boolean(value)};
7550
+ default:
7551
+ meta.logger.warn(`zigbee-herdsman-converters:tuya_smart_vibration_sensor: NOT RECOGNIZED ` +
7552
+ `DP #${dp} with data ${JSON.stringify(msg.data)}`);
7553
+ }
7554
+ },
7555
+ },
7440
7556
  moes_thermostat_tv: {
7441
7557
  cluster: 'manuSpecificTuya',
7442
7558
  type: ['commandDataResponse', 'commandDataReport', 'raw'],
@@ -7527,7 +7643,11 @@ const converters = {
7527
7643
  cluster: 'genAnalogInput',
7528
7644
  type: ['attributeReport', 'readResponse'],
7529
7645
  convert: (model, msg, publish, options, meta) => {
7530
- return {people: msg.data.presentValue};
7646
+ const lookup = {'0': 'idle', '1': 'in', '2': 'out'};
7647
+ const value = precisionRound(parseFloat(msg.data['presentValue']), 1);
7648
+ let result = null;
7649
+ result = {people: precisionRound(msg.data.presentValue, 0), status: lookup[value*10%10]};
7650
+ return result;
7531
7651
  },
7532
7652
  },
7533
7653
  sihas_action: {
@@ -7712,6 +7832,40 @@ const converters = {
7712
7832
  };
7713
7833
  },
7714
7834
  },
7835
+ tuya_light_wz5: {
7836
+ cluster: 'manuSpecificTuya',
7837
+ type: ['commandDataResponse', 'commandDataReport'],
7838
+ convert: (model, msg, publish, options, meta) => {
7839
+ const separateWhite = (model.meta && model.meta.separateWhite);
7840
+ const result = {};
7841
+ // eslint-disable-next-line no-unused-vars
7842
+ for (const [i, dpValue] of msg.data.dpValues.entries()) {
7843
+ const dp = dpValue.dp;
7844
+ const value = tuya.getDataValue(dpValue);
7845
+ if (dp === tuya.dataPoints.state) {
7846
+ result.state = value ? 'ON': 'OFF';
7847
+ } else if (dp === tuya.dataPoints.silvercrestSetBrightness) {
7848
+ const brightness = mapNumberRange(value, 0, 1000, 0, 255);
7849
+ if (separateWhite) {
7850
+ result.white_brightness = brightness;
7851
+ } else {
7852
+ result.brightness = brightness;
7853
+ }
7854
+ } else if (dp === tuya.dataPoints.silvercrestSetColor) {
7855
+ const h = parseInt(value.substring(0, 4), 16);
7856
+ const s = parseInt(value.substring(4, 8), 16);
7857
+ const b = parseInt(value.substring(8, 12), 16);
7858
+ result.color_mode = 'hs';
7859
+ result.color = {hue: h, saturation: mapNumberRange(s, 0, 1000, 0, 100)};
7860
+ result.brightness = mapNumberRange(b, 0, 1000, 0, 255);
7861
+ } else if (dp === tuya.dataPoints.silvercrestSetColorTemp) {
7862
+ const [colorTempMin, colorTempMax] = [250, 454];
7863
+ result.color_temp = mapNumberRange(value, 0, 1000, colorTempMax, colorTempMin);
7864
+ }
7865
+ }
7866
+ return result;
7867
+ },
7868
+ },
7715
7869
  // #endregion
7716
7870
 
7717
7871
  // #region Ignore converters (these message dont need parsing).
@@ -7865,6 +8019,11 @@ const converters = {
7865
8019
  type: ['commandMcuSyncTime'],
7866
8020
  convert: (model, msg, publish, options, meta) => null,
7867
8021
  },
8022
+ ignore_tuya_raw: {
8023
+ cluster: 'manuSpecificTuya',
8024
+ type: ['raw'],
8025
+ convert: (model, msg, publish, options, meta) => null,
8026
+ },
7868
8027
  // #endregion
7869
8028
  };
7870
8029
 
@@ -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,12 +2078,12 @@ const converters = {
2076
2078
  await entity.read('aqaraOpple', [0x0000], manufacturerOptions.xiaomi);
2077
2079
  },
2078
2080
  },
2079
- RTCGQ13LM_detection_interval: {
2080
- key: ['detection_interval'],
2081
+ aqara_occupancy_timeout: {
2082
+ key: ['occupancy_timeout'],
2081
2083
  convertSet: async (entity, key, value, meta) => {
2082
2084
  value *= 1;
2083
2085
  await entity.write('aqaraOpple', {0x0102: {value: [value], type: 0x20}}, manufacturerOptions.xiaomi);
2084
- return {state: {detection_interval: value}};
2086
+ return {state: {occupancy_timeout: value}};
2085
2087
  },
2086
2088
  convertGet: async (entity, key, meta) => {
2087
2089
  await entity.read('aqaraOpple', [0x0102], manufacturerOptions.xiaomi);
@@ -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/aldi.js CHANGED
@@ -12,14 +12,6 @@ module.exports = [
12
12
  extend: extend.light_onoff_brightness_colortemp_color({disableColorTempStartup: true}),
13
13
  meta: {applyRedFix: true},
14
14
  },
15
- {
16
- fingerprint: [{modelID: 'TS0505B', manufacturerName: '_TZ3000_kohbva1f'}],
17
- model: 'L122CB63H11A9.0W',
18
- vendor: 'Aldi',
19
- description: 'LIGHTWAY smart home LED-lamp - bulb',
20
- extend: extend.light_onoff_brightness_colortemp_color({disableColorTempStartup: true}),
21
- meta: {applyRedFix: true},
22
- },
23
15
  {
24
16
  fingerprint: [{modelID: 'TS0505B', manufacturerName: '_TZ3000_iivsrikg'}],
25
17
  model: 'L122AA63H11A6.5W',
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]},
@@ -14,7 +14,11 @@ module.exports = [
14
14
  description: 'Light switch with neutral',
15
15
  fromZigbee: [fz.identify, fz.on_off, fz.K4003C_binary_input],
16
16
  toZigbee: [tz.on_off, tz.legrand_settingAlwaysEnableLed, tz.legrand_settingEnableLedIfOn, tz.legrand_identify],
17
- exposes: [e.switch(), e.action(['identify', 'on', 'off'])],
17
+ exposes: [
18
+ e.switch(), e.action(['identify', 'on', 'off']),
19
+ exposes.binary('permanent_led', ea.STATE_SET, 'ON', 'OFF').withDescription('Enable or disable the permanent blue LED'),
20
+ exposes.binary('led_when_on', ea.STATE_SET, 'ON', 'OFF').withDescription('Enables the LED when the light is on'),
21
+ ],
18
22
  configure: async (device, coordinatorEndpoint, logger) => {
19
23
  const endpoint = device.getEndpoint(1);
20
24
  await reporting.bind(endpoint, coordinatorEndpoint, ['genIdentify', 'genOnOff', 'genBinaryInput']);
@@ -26,7 +30,12 @@ module.exports = [
26
30
  vendor: 'BTicino',
27
31
  description: 'Dimmer switch with neutral',
28
32
  extend: extend.light_onoff_brightness({noConfigure: true}),
29
- exposes: [e.light_brightness()],
33
+ exposes: [
34
+ e.light_brightness(),
35
+ exposes.binary('dimmer_enabled', ea.STATE_SET, 'ON', 'OFF').withDescription('Allow the device to change brightness'),
36
+ exposes.binary('permanent_led', ea.STATE_SET, 'ON', 'OFF').withDescription('Enable or disable the permanent blue LED'),
37
+ exposes.binary('led_when_on', ea.STATE_SET, 'ON', 'OFF').withDescription('Enables the LED when the light is on'),
38
+ ],
30
39
  fromZigbee: [fz.brightness, fz.identify, fz.on_off],
31
40
  toZigbee: [tz.light_onoff_brightness, tz.legrand_settingAlwaysEnableLed, tz.legrand_settingEnableLedIfOn,
32
41
  tz.legrand_settingEnableDimmer, tz.legrand_identify],
@@ -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/ecodim.js CHANGED
@@ -121,4 +121,11 @@ module.exports = [
121
121
  toZigbee: [],
122
122
  meta: {multiEndpoint: true, battery: {dontDividePercentage: true}},
123
123
  },
124
+ {
125
+ fingerprint: [{modelID: 'TS0501B', manufacturerName: '_TZ3210_yluvwhjc'}],
126
+ model: 'ED-10042',
127
+ vendor: 'EcoDim',
128
+ description: 'Zigbee LED filament light dimmable E27, globe G125, flame 2200K',
129
+ extend: extend.light_onoff_brightness(),
130
+ },
124
131
  ];
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
@@ -74,7 +74,7 @@ module.exports = [
74
74
  exposes: [e.contact(), e.battery_low(), e.tamper(), e.temperature(), e.battery()],
75
75
  },
76
76
  {
77
- zigbeeModel: ['3450-L'],
77
+ zigbeeModel: ['3450-L', '3450-L2'],
78
78
  model: '3450-L',
79
79
  vendor: 'Iris',
80
80
  description: 'Smart fob',
package/devices/konke.js CHANGED
@@ -73,4 +73,13 @@ module.exports = [
73
73
  toZigbee: [],
74
74
  exposes: [e.water_leak(), e.battery_low(), e.tamper()],
75
75
  },
76
+ {
77
+ fingerprint: [{modelID: 'TS0222', manufacturerName: '_TYZB01_fi5yftwv'}],
78
+ model: 'KK-ES-J01W',
79
+ vendor: 'Konke',
80
+ description: 'Room temperature, relative humidity and illuminance sensor',
81
+ fromZigbee: [fz.battery, fz.illuminance, fz.humidity, fz.temperature],
82
+ toZigbee: [],
83
+ exposes: [e.battery(), e.illuminance(), e.illuminance_lux().withUnit('lx'), e.humidity(), e.temperature()],
84
+ },
76
85
  ];
package/devices/nodon.js CHANGED
@@ -16,6 +16,8 @@ module.exports = [
16
16
  configure: async (device, coordinatorEndpoint, logger) => {
17
17
  const endpoint = device.getEndpoint(1);
18
18
  await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'closuresWindowCovering']);
19
+ await reporting.currentPositionLiftPercentage(endpoint);
20
+ await reporting.currentPositionTiltPercentage(endpoint);
19
21
  },
20
22
  exposes: [e.cover_position()],
21
23
  },
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
@@ -178,11 +178,11 @@ module.exports = [
178
178
  ota: ota.ledvance,
179
179
  },
180
180
  {
181
- zigbeeModel: ['Surface Light TW'],
181
+ zigbeeModel: ['Surface Light TW', 'ZLO-CeilingTW-OS'],
182
182
  model: 'AB401130055',
183
183
  vendor: 'OSRAM',
184
184
  description: 'LIGHTIFY Surface Light LED Tunable White',
185
- extend: extend.ledvance.light_onoff_brightness_colortemp(),
185
+ extend: extend.ledvance.light_onoff_brightness_colortemp({colorTempRange: [153, 370]}),
186
186
  ota: ota.ledvance,
187
187
  },
188
188
  {
package/devices/owon.js CHANGED
@@ -106,15 +106,14 @@ module.exports = [
106
106
  zigbeeModel: ['THS317-ET'],
107
107
  model: 'THS317-ET',
108
108
  vendor: 'OWON',
109
- description: 'Temperature and humidity sensor',
110
- fromZigbee: [fz.temperature, fz.humidity, fz.battery],
109
+ description: 'Temperature sensor',
110
+ fromZigbee: [fz.temperature, fz.battery],
111
111
  toZigbee: [],
112
- exposes: [e.battery(), e.temperature(), e.humidity()],
112
+ exposes: [e.battery(), e.temperature()],
113
113
  configure: async (device, coordinatorEndpoint, logger) => {
114
- const endpoint = device.getEndpoint(2);
115
- await reporting.bind(endpoint, coordinatorEndpoint, ['msTemperatureMeasurement', 'msRelativeHumidity', 'genPowerCfg']);
114
+ const endpoint = device.getEndpoint(3);
115
+ await reporting.bind(endpoint, coordinatorEndpoint, ['msTemperatureMeasurement', 'genPowerCfg']);
116
116
  await reporting.temperature(endpoint);
117
- await reporting.humidity(endpoint);
118
117
  await reporting.batteryVoltage(endpoint);
119
118
  await reporting.batteryPercentageRemaining(endpoint);
120
119
  device.powerSource = 'Battery';
@@ -29,6 +29,24 @@ 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
+ },
41
+ {
42
+ zigbeeModel: ['915005996401'],
43
+ model: '915005996401',
44
+ vendor: 'Philips',
45
+ description: 'Hue white ambiance ceiling light Enrave S with Bluetooth',
46
+ meta: {turnsOffAtBrightness1: true},
47
+ extend: hueExtend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
48
+ ota: ota.zigbeeOTA,
49
+ },
32
50
  {
33
51
  zigbeeModel: ['929003054001'],
34
52
  model: '929003054001',
@@ -2315,7 +2333,7 @@ module.exports = [
2315
2333
  ota: ota.zigbeeOTA,
2316
2334
  },
2317
2335
  {
2318
- zigbeeModel: ['5309331P6'],
2336
+ zigbeeModel: ['5309331P6', '5309330P6'],
2319
2337
  model: '5309331P6',
2320
2338
  vendor: 'Philips',
2321
2339
  description: 'Hue White ambiance Runner triple spotlight',
@@ -2324,7 +2342,7 @@ module.exports = [
2324
2342
  ota: ota.zigbeeOTA,
2325
2343
  },
2326
2344
  {
2327
- zigbeeModel: ['5309230P6'],
2345
+ zigbeeModel: ['5309230P6', '5309231P6'],
2328
2346
  model: '5309230P6',
2329
2347
  vendor: 'Philips',
2330
2348
  description: 'Hue White ambiance Runner double spotlight',
@@ -2369,7 +2387,7 @@ module.exports = [
2369
2387
  ota: ota.zigbeeOTA,
2370
2388
  },
2371
2389
  {
2372
- zigbeeModel: ['5309030P9'],
2390
+ zigbeeModel: ['5309030P9', '5309031P9', '5309030P6', '5309031P6'],
2373
2391
  model: '5309030P9',
2374
2392
  vendor: 'Philips',
2375
2393
  description: 'Hue White ambiance Runner single spotlight',
@@ -610,4 +610,24 @@ module.exports = [
610
610
  await reporting.readMeteringMultiplierDivisor(endpoint);
611
611
  },
612
612
  },
613
+ {
614
+ zigbeeModel: ['LK/OUTLET/1'],
615
+ model: '545D6115',
616
+ vendor: 'Schneider Electric',
617
+ description: 'LK FUGA wiser wireless socket outlet',
618
+ fromZigbee: [fz.on_off, fz.electrical_measurement, fz.EKO09738_metering, fz.power_on_behavior],
619
+ toZigbee: [tz.on_off, tz.power_on_behavior],
620
+ exposes: [e.switch(), e.power(), e.energy(), e.current(), e.voltage(),
621
+ exposes.enum('power_on_behavior', ea.ALL, ['off', 'previous', 'on'])
622
+ .withDescription('Controls the behaviour when the device is powered on')],
623
+ configure: async (device, coordinatorEndpoint, logger) => {
624
+ const endpoint = device.getEndpoint(6);
625
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'haElectricalMeasurement', 'seMetering']);
626
+ await reporting.onOff(endpoint);
627
+ // Unit supports acVoltage and acCurrent, but only acCurrent divisor/multiplier can be read
628
+ await endpoint.read('haElectricalMeasurement', ['acCurrentDivisor', 'acCurrentMultiplier']);
629
+ await reporting.readMeteringMultiplierDivisor(endpoint);
630
+ await reporting.currentSummDelivered(endpoint, {min: 60, change: 1});
631
+ },
632
+ },
613
633
  ];
@@ -24,6 +24,7 @@ module.exports = [
24
24
  await endpoint.configureReporting('genAnalogInput', payload);
25
25
  },
26
26
  exposes: [e.battery(), e.battery_voltage(),
27
+ exposes.enum('status', ea.STATE, ['idle', 'in', 'out']).withDescription('Currently status'),
27
28
  exposes.numeric('people', ea.ALL).withValueMin(0).withValueMax(50).withDescription('People count')],
28
29
  },
29
30
  {
@@ -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
+ ];
@@ -6,6 +6,22 @@ const extend = require('../lib/extend');
6
6
  const e = exposes.presets;
7
7
 
8
8
  module.exports = [
9
+ {
10
+ zigbeeModel: ['ON/OFF(2CH)'],
11
+ model: 'UP-SA-9127D',
12
+ vendor: 'Sunricher',
13
+ description: 'LED-trading 2 channel AC switch',
14
+ extend: extend.switch(),
15
+ exposes: [e.switch().withEndpoint('l1'), e.switch().withEndpoint('l2')],
16
+ endpoint: (device) => {
17
+ return {'l1': 1, 'l2': 2};
18
+ },
19
+ meta: {multiEndpoint: true},
20
+ configure: async (device, coordinatorEndpoint, logger) => {
21
+ await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff']);
22
+ await reporting.bind(device.getEndpoint(2), coordinatorEndpoint, ['genOnOff']);
23
+ },
24
+ },
9
25
  {
10
26
  zigbeeModel: ['HK-ZD-CCT-A'],
11
27
  model: 'HK-ZD-CCT-A',
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: {
@@ -158,6 +158,7 @@ module.exports = [
158
158
  {modelID: 'TS0505B', manufacturerName: '_TZ3000_589kq4ul'},
159
159
  {modelID: 'TS0505B', manufacturerName: '_TZ3000_1mtktxdk'},
160
160
  {modelID: 'TS0505B', manufacturerName: '_TZ3210_remypqqm'},
161
+ {modelID: 'TS0505B', manufacturerName: '_TZ3000_kohbva1f'},
161
162
  {modelID: 'TS0505B', manufacturerName: '_TZ3210_0rn9qhnu'},
162
163
  {modelID: 'TS0505B', manufacturerName: '_TZ3210_bicjqpg4'},
163
164
  {modelID: 'TS0505B', manufacturerName: '_TZ3000_cmaky9gq'},
@@ -166,7 +167,10 @@ module.exports = [
166
167
  vendor: 'TuYa',
167
168
  description: 'Zigbee RGB+CCT light',
168
169
  whiteLabel: [{vendor: 'Mercator Ikuü', model: 'SMD4106W-RGB-ZB'},
169
- {vendor: 'TuYa', model: 'A5C-21F7-01'}, {vendor: 'Mercator Ikuü', model: 'S9E27LED9W-RGB-Z'}],
170
+ {vendor: 'TuYa', model: 'A5C-21F7-01'}, {vendor: 'Mercator Ikuü', model: 'S9E27LED9W-RGB-Z'},
171
+ {vendor: 'Aldi', model: 'L122CB63H11A9.0W', description: 'LIGHTWAY smart home LED-lamp - bulb'},
172
+ {vendor: 'Lidl', model: '14153706L', description: 'Livarno smart LED ceiling light'},
173
+ ],
170
174
  extend: extend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
171
175
  meta: {applyRedFix: true, enhancedHue: false},
172
176
  },
@@ -660,6 +664,20 @@ module.exports = [
660
664
  toZigbee: [],
661
665
  whiteLabel: [{vendor: 'Neo', model: 'NAS-WS02B0'}],
662
666
  },
667
+ {
668
+ fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_jthf7vb6'}],
669
+ model: 'WLS-100z',
670
+ vendor: 'TuYa',
671
+ description: 'Water leak sensor',
672
+ fromZigbee: [fz.ignore_basic_report, fz.ignore_tuya_raw, fz.wls100z_water_leak],
673
+ toZigbee: [],
674
+ onEvent: tuya.onEventSetTime,
675
+ configure: async (device, coordinatorEndpoint, logger) => {
676
+ const endpoint = device.getEndpoint(1);
677
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genBasic']);
678
+ },
679
+ exposes: [e.battery(), e.water_leak()],
680
+ },
663
681
  {
664
682
  zigbeeModel: ['TS0001'],
665
683
  model: 'TS0001',
@@ -1151,7 +1169,8 @@ module.exports = [
1151
1169
  },
1152
1170
  {
1153
1171
  fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_byzdayie'},
1154
- {modelID: 'TS0601', manufacturerName: '_TZE200_fsb6zw01'}],
1172
+ {modelID: 'TS0601', manufacturerName: '_TZE200_fsb6zw01'},
1173
+ {modelID: 'TS0601', manufacturerName: '_TZE200_ewxhg6o9'}],
1155
1174
  model: 'TS0601_din',
1156
1175
  vendor: 'TuYa',
1157
1176
  description: 'Zigbee smart energy meter DDS238-2 Zigbee',
@@ -1677,7 +1696,9 @@ module.exports = [
1677
1696
  ],
1678
1697
  },
1679
1698
  {
1680
- fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_vrfecyku'}],
1699
+ fingerprint: [
1700
+ {modelID: 'TS0601', manufacturerName: '_TZE200_vrfecyku'},
1701
+ {modelID: 'TS0601', manufacturerName: '_TZE200_lu01t0zl'}],
1681
1702
  model: 'MIR-HE200-TY',
1682
1703
  vendor: 'TuYa',
1683
1704
  description: 'Human presence sensor',
@@ -1805,4 +1826,13 @@ module.exports = [
1805
1826
  toZigbee: [],
1806
1827
  exposes: [e.action(['toggle', 'brightness_step_up', 'brightness_step_down'])],
1807
1828
  },
1829
+ {
1830
+ fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_kzm5w4iz'}],
1831
+ model: 'TS0601_vibration_sensor',
1832
+ vendor: 'TuYa',
1833
+ description: 'Smart vibration sensor',
1834
+ fromZigbee: [fz.tuya_smart_vibration_sensor],
1835
+ toZigbee: [],
1836
+ exposes: [e.contact(), e.battery(), e.vibration()],
1837
+ },
1808
1838
  ];
package/devices/xiaomi.js CHANGED
@@ -89,6 +89,22 @@ module.exports = [
89
89
  e.power_outage_memory().withAccess(ea.STATE_SET)]),
90
90
  ota: ota.zigbeeOTA,
91
91
  },
92
+ {
93
+ zigbeeModel: ['lumi.light.acn003'],
94
+ model: 'ZNXDD01LM',
95
+ vendor: 'Xiaomi',
96
+ description: 'Aqara ceiling light L1-350',
97
+ extend: xiaomiExtend.light_onoff_brightness_colortemp({disableEffect: true, colorTempRange: [153, 370]}),
98
+ ota: ota.zigbeeOTA,
99
+ },
100
+ {
101
+ zigbeeModel: ['lumi.light.cwac02'],
102
+ model: 'ZNLDP13LM',
103
+ vendor: 'Xiaomi',
104
+ description: 'Aqara T1 smart LED bulb',
105
+ extend: xiaomiExtend.light_onoff_brightness_colortemp({disableEffect: true, colorTempRange: [153, 370]}),
106
+ ota: ota.zigbeeOTA,
107
+ },
92
108
  {
93
109
  zigbeeModel: ['lumi.light.cwopcn01'],
94
110
  model: 'XDD11LM',
@@ -670,6 +686,11 @@ module.exports = [
670
686
  exposes.enum('operation_mode', ea.ALL, ['control_relay', 'decoupled'])
671
687
  .withDescription('Decoupled mode'),
672
688
  ],
689
+ configure: async (device, coordinatorEndpoint, logger) => {
690
+ device.type = 'Router';
691
+ device.powerSource = 'Mains (single phase)';
692
+ device.save();
693
+ },
673
694
  },
674
695
  {
675
696
  zigbeeModel: ['lumi.switch.b2nacn02'],
@@ -852,19 +873,17 @@ module.exports = [
852
873
  zigbeeModel: ['lumi.motion.agl02'],
853
874
  model: 'RTCGQ12LM',
854
875
  vendor: 'Xiaomi',
855
- description: 'Aqara T1 human body movement and illuminance sensor (illuminance not supported for now)',
856
- fromZigbee: [fz.occupancy, fz.occupancy_timeout, fz.battery],
857
- toZigbee: [tz.occupancy_timeout],
858
- exposes: [e.occupancy(), e.battery(),
859
- exposes.numeric('occupancy_timeout', exposes.access.ALL).withValueMin(0).withValueMax(65535).withUnit('s')
860
- .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_occupancy_timeout],
879
+ exposes: [e.occupancy(), e.illuminance().withUnit('lx').withDescription('Measured illuminance in lux'),
880
+ exposes.numeric('occupancy_timeout', ea.ALL).withValueMin(2).withValueMax(65535).withUnit('s')
881
+ .withDescription('Time in seconds till occupancy goes to false'), e.battery()],
861
882
  meta: {battery: {voltageToPercentage: '3V_2100'}},
862
883
  configure: async (device, coordinatorEndpoint, logger) => {
863
884
  const endpoint = device.getEndpoint(1);
864
- await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'msOccupancySensing']);
865
- await reporting.occupancy(endpoint);
866
- await reporting.batteryVoltage(endpoint);
867
- await endpoint.read('msOccupancySensing', ['pirOToUDelay']);
885
+ await endpoint.read('genPowerCfg', ['batteryVoltage']);
886
+ await endpoint.read('aqaraOpple', [0x0102], {manufacturerCode: 0x115f});
868
887
  },
869
888
  },
870
889
  {
@@ -872,15 +891,15 @@ module.exports = [
872
891
  model: 'RTCGQ13LM',
873
892
  vendor: 'Xiaomi',
874
893
  description: 'Aqara high precision motion sensor',
875
- fromZigbee: [fz.occupancy_with_timeout, fz.aqara_opple, fz.battery],
876
- toZigbee: [tz.RTCGQ13LM_detection_interval, tz.RTCGQ13LM_motion_sensitivity],
877
- exposes: [e.occupancy(), e.battery(),
878
- exposes.enum('motion_sensitivity', exposes.access.ALL, ['low', 'medium', 'high']),
879
- exposes.numeric('detection_interval', exposes.access.ALL).withValueMin(2).withValueMax(180).withUnit('s')
880
- .withDescription('Time interval for detecting actions')],
894
+ fromZigbee: [fz.RTCGQ13LM_occupancy, fz.aqara_opple, fz.battery],
895
+ toZigbee: [tz.aqara_occupancy_timeout, tz.RTCGQ13LM_motion_sensitivity],
896
+ exposes: [e.occupancy(), exposes.enum('motion_sensitivity', ea.ALL, ['low', 'medium', 'high']),
897
+ exposes.numeric('occupancy_timeout', ea.ALL).withValueMin(2).withValueMax(65535).withUnit('s')
898
+ .withDescription('Time in seconds till occupancy goes to false'), e.battery()],
881
899
  meta: {battery: {voltageToPercentage: '3V_2100'}},
882
900
  configure: async (device, coordinatorEndpoint, logger) => {
883
901
  const endpoint = device.getEndpoint(1);
902
+ await endpoint.read('genPowerCfg', ['batteryVoltage']);
884
903
  await endpoint.read('aqaraOpple', [0x0102], {manufacturerCode: 0x115f});
885
904
  await endpoint.read('aqaraOpple', [0x010c], {manufacturerCode: 0x115f});
886
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)'),
@@ -2,17 +2,56 @@ const url = 'https://raw.githubusercontent.com/Koenkk/zigbee-OTA/master/index.js
2
2
  const assert = require('assert');
3
3
  const common = require('./common');
4
4
  const axios = common.getAxios();
5
+ const fs = require('fs');
6
+ const URI = require('uri-js');
7
+
8
+ let overrideFileName = null;
5
9
 
6
10
  /**
7
11
  * Helper functions
8
12
  */
9
13
 
14
+
15
+ function isValidUrl(url) {
16
+ let parsed;
17
+ try {
18
+ parsed = URI.parse(url);
19
+ } catch (_) {
20
+ return false;
21
+ }
22
+ return parsed.scheme === 'http' || parsed.scheme === 'https';
23
+ }
24
+
25
+ async function getFile(urlOrName) {
26
+ if (isValidUrl(urlOrName)) {
27
+ return (await axios.get(urlOrName)).data;
28
+ }
29
+
30
+ return JSON.parse(fs.readFileSync(urlOrName));
31
+ }
32
+
33
+ async function getIndex(logger) {
34
+ const index = (await axios.get(url)).data;
35
+
36
+ logger.debug(`ZigbeeOTA: downloaded main index`);
37
+
38
+ if (overrideFileName) {
39
+ logger.debug(`ZigbeeOTA: Loading override index ${overrideFileName}`);
40
+ const localIndex = await getFile(overrideFileName);
41
+
42
+ // Resulting index will have overriden items first
43
+ return localIndex.concat(index);
44
+ }
45
+
46
+ return index;
47
+ }
48
+
10
49
  async function getImageMeta(current, logger, device) {
11
50
  const modelId = device.modelID;
12
51
  const imageType = current.imageType;
13
52
  const manufacturerCode = current.manufacturerCode;
14
53
  const manufacturerName = device.manufacturerName;
15
- const images = (await axios.get(url)).data;
54
+ const images = await getIndex(logger);
16
55
 
17
56
  // NOTE: Officially an image can be determined with a combination of manufacturerCode and imageType.
18
57
  // However Gledopto pro products use the same imageType (0) for every device while the image is different.
@@ -46,4 +85,7 @@ module.exports = {
46
85
  getImageMeta,
47
86
  isUpdateAvailable,
48
87
  updateToLatest,
88
+ useIndexOverride: (indexFileName) => {
89
+ overrideFileName = indexFileName;
90
+ },
49
91
  };
package/lib/tuya.js CHANGED
@@ -86,6 +86,9 @@ async function onEventMeasurementPoll(type, data, device, options) {
86
86
  try {
87
87
  await endpoint.read('haElectricalMeasurement', ['rmsVoltage', 'rmsCurrent', 'activePower']);
88
88
  await endpoint.read('seMetering', ['currentSummDelivered']);
89
+ if (device.manufacturerName === '_TZ3000_u5u4cakc') {
90
+ await endpoint.read('genOnOff', ['onOff']);
91
+ }
89
92
  } catch (error) {/* Do nothing*/}
90
93
  }, seconds*1000);
91
94
  globalStore.putValue(device, 'interval', interval);
@@ -381,6 +384,7 @@ const dataPoints = {
381
384
  // Silvercrest
382
385
  silvercrestChangeMode: 2,
383
386
  silvercrestSetBrightness: 3,
387
+ silvercrestSetColorTemp: 4,
384
388
  silvercrestSetColor: 5,
385
389
  silvercrestSetEffect: 6,
386
390
  // Fantem
@@ -521,12 +525,16 @@ const dataPoints = {
521
525
  nousMinTemp: 11,
522
526
  nousTempAlarm: 14,
523
527
  nousTempSensitivity: 19,
524
-
525
528
  // TUYA / HUMIDITY/ILLUMINANCE/TEMPERATURE SENSOR
526
529
  thitBatteryPercentage: 3,
527
530
  thitIlluminanceLux: 7,
528
531
  thitHumidity: 9,
529
532
  thitTemperature: 8,
533
+ // TUYA SMART VIBRATION SENSOR
534
+ tuyaVibration: 10,
535
+ // TUYA WLS-100z Water Leak Sensor
536
+ wlsWaterLeak: 1,
537
+ wlsBatteryPercentage: 4,
530
538
  };
531
539
 
532
540
  const thermostatWeekFormat = {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "14.0.369",
3
+ "version": "14.0.373",
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.369",
3
+ "version": "14.0.373",
4
4
  "description": "Collection of device converters to be used with zigbee-herdsman",
5
5
  "main": "index.js",
6
6
  "files": [