zigbee-herdsman-converters 14.0.498 → 14.0.501

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.
@@ -2562,42 +2562,51 @@ const converters = {
2562
2562
  const dpValue = tuya.firstDpValue(msg, meta, 'neo_nas_pd07');
2563
2563
  const dp = dpValue.dp;
2564
2564
  const value = tuya.getDataValue(dpValue);
2565
- if (dp === 101) return {occupancy: value > 0 ? true : false};
2566
- else if (dp === 102) {
2565
+ switch (dp) {
2566
+ case tuya.dataPoints.neoOccupancy:
2567
+ return {occupancy: value > 0 ? true : false};
2568
+ case 102:
2567
2569
  return {
2568
2570
  power_type: {0: 'battery_full', 1: 'battery_high', 2: 'battery_medium', 3: 'battery_low', 4: 'usb'}[value],
2569
2571
  battery_low: value === 3,
2570
2572
  };
2571
- } else if (dp === 103) {
2573
+ case tuya.dataPoints.neoTamper:
2572
2574
  return {tamper: value > 0 ? true : false};
2573
- } else if (dp === 104) {
2575
+ case 104:
2574
2576
  return {temperature: calibrateAndPrecisionRoundOptions(value / 10, options, 'temperature')};
2575
- } else if (dp === 105) {
2577
+ case 105:
2576
2578
  return {humidity: calibrateAndPrecisionRoundOptions(value, options, 'humidity')};
2577
- } else if (dp === tuya.dataPoints.neoMinTemp) {
2579
+ case tuya.dataPoints.neoMinTemp:
2578
2580
  return {temperature_min: value};
2579
- } else if (dp === tuya.dataPoints.neoMaxTemp) {
2581
+ case tuya.dataPoints.neoMaxTemp:
2580
2582
  return {temperature_max: value};
2581
- } else if (dp === tuya.dataPoints.neoMinHumidity) {
2583
+ case tuya.dataPoints.neoMinHumidity:
2582
2584
  return {humidity_min: value};
2583
- } else if (dp === tuya.dataPoints.neoMaxHumidity) {
2585
+ case tuya.dataPoints.neoMaxHumidity:
2584
2586
  return {humidity_max: value};
2585
- } else if (dp === 113) {
2587
+ case tuya.dataPoints.neoTempScale:
2588
+ return {temperature_scale: value ? '°C' : '°F'};
2589
+ case 111:
2590
+ return {unknown_111: value ? 'ON' : 'OFF'};
2591
+ case 112:
2592
+ return {unknown_112: value ? 'ON' : 'OFF'};
2593
+ case tuya.dataPoints.neoTempHumidityAlarm:
2586
2594
  return {alarm: {0: 'over_temperature', 1: 'over_humidity',
2587
2595
  2: 'below_min_temperature', 3: 'below_min_humdity', 4: 'off'}[value]};
2588
- } else {
2589
- meta.logger.warn(`fromZigbee.neo_nas_pd07: Unrecognized DP #${dp} with data ${JSON.stringify(dpValue)}`);
2596
+ default: // Unknown code
2597
+ meta.logger.warn(`fz.neo_nas_pd07: Unhandled DP #${dp}: ${JSON.stringify(dpValue)}`);
2590
2598
  }
2591
2599
  },
2592
2600
  },
2593
2601
  neo_t_h_alarm: {
2594
2602
  cluster: 'manuSpecificTuya',
2595
2603
  type: ['commandDataReport', 'commandDataResponse'],
2604
+ options: [exposes.options.precision('temperature'), exposes.options.calibration('temperature'),
2605
+ exposes.options.precision('humidity'), exposes.options.calibration('humidity')],
2596
2606
  convert: (model, msg, publish, options, meta) => {
2597
2607
  const dpValue = tuya.firstDpValue(msg, meta, 'neo_t_h_alarm');
2598
2608
  const dp = dpValue.dp;
2599
2609
  const value = tuya.getDataValue(dpValue);
2600
-
2601
2610
  switch (dp) {
2602
2611
  case tuya.dataPoints.neoAlarm:
2603
2612
  return {alarm: value};
@@ -2610,9 +2619,9 @@ const converters = {
2610
2619
  case tuya.dataPoints.neoDuration: // 0x0267 [0,0,0,10] duration alarm in second
2611
2620
  return {duration: value};
2612
2621
  case tuya.dataPoints.neoTemp: // 0x0269 [0,0,0,240] temperature
2613
- return {temperature: (value / 10).toFixed(1)};
2622
+ return {temperature: calibrateAndPrecisionRoundOptions((value / 10), options, 'temperature')};
2614
2623
  case tuya.dataPoints.neoHumidity: // 0x026A [0,0,0,36] humidity
2615
- return {humidity: value};
2624
+ return {humidity: calibrateAndPrecisionRoundOptions(value, options, 'humidity')};
2616
2625
  case tuya.dataPoints.neoMinTemp: // 0x026B [0,0,0,18] min alarm temperature
2617
2626
  return {temperature_min: value};
2618
2627
  case tuya.dataPoints.neoMaxTemp: // 0x026C [0,0,0,27] max alarm temperature
@@ -2633,7 +2642,7 @@ const converters = {
2633
2642
  case tuya.dataPoints.neoVolume: // 0x0474 [0]/[1]/[2] Volume 0-max, 2-low
2634
2643
  return {volume: {2: 'low', 1: 'medium', 0: 'high'}[value]};
2635
2644
  default: // Unknown code
2636
- meta.logger.warn(`Unhandled DP #${dp}: ${JSON.stringify(dpValue)}`);
2645
+ meta.logger.warn(`fz.neo_t_h_alarm: Unhandled DP #${dp}: ${JSON.stringify(dpValue)}`);
2637
2646
  }
2638
2647
  },
2639
2648
  },
@@ -4309,14 +4318,19 @@ const converters = {
4309
4318
  // DP2: Smart Air Box: co2, Smart Air Housekeeper: MP25
4310
4319
  case tuya.dataPoints.tuyaSabCO2:
4311
4320
  if (meta.device.manufacturerName === '_TZE200_dwcarsat') {
4321
+ if (value === 0xaaab) return; // Ignore: https://github.com/Koenkk/zigbee2mqtt/issues/11033#issuecomment-1109808552
4312
4322
  return {pm25: calibrateAndPrecisionRoundOptions(value, options, 'pm25')};
4313
4323
  } else if (meta.device.manufacturerName === '_TZE200_ryfmq5rl') {
4314
- return {formaldehyd: calibrateAndPrecisionRoundOptions(value, options, 'formaldehyd')};
4324
+ return {formaldehyd: calibrateAndPrecisionRoundOptions(value, options, 'formaldehyd') / 100};
4315
4325
  } else {
4316
4326
  return {co2: calibrateAndPrecisionRoundOptions(value, options, 'co2')};
4317
4327
  }
4318
4328
  case tuya.dataPoints.tuyaSabVOC:
4319
- return {voc: calibrateAndPrecisionRoundOptions(value, options, 'voc')};
4329
+ if (meta.device.manufacturerName === '_TZE200_ryfmq5rl') {
4330
+ return {voc: calibrateAndPrecisionRoundOptions(value, options, 'voc') / 10};
4331
+ } else {
4332
+ return {voc: calibrateAndPrecisionRoundOptions(value, options, 'voc')};
4333
+ }
4320
4334
  case tuya.dataPoints.tuyaSahkFormaldehyd:
4321
4335
  return {formaldehyd: calibrateAndPrecisionRoundOptions(value, options, 'formaldehyd')};
4322
4336
  default:
@@ -5872,19 +5886,9 @@ const converters = {
5872
5886
  return result;
5873
5887
  },
5874
5888
  },
5875
- xiaomi_curtain_acn002_position: {
5876
- cluster: 'genAnalogOutput',
5877
- type: ['attributeReport', 'readResponse'],
5878
- options: [exposes.options.invert_cover()],
5879
- convert: (model, msg, publish, options, meta) => {
5880
- let position = precisionRound(msg.data['presentValue'], 2);
5881
- position = options.invert_cover ? 100 - position : position;
5882
- return {position: position};
5883
- },
5884
- },
5885
5889
  xiaomi_curtain_acn002_status: {
5886
5890
  cluster: 'genMultistateOutput',
5887
- type: ['attributeReport', 'readResponse'],
5891
+ type: ['attributeReport'],
5888
5892
  convert: (model, msg, publish, options, meta) => {
5889
5893
  let running = false;
5890
5894
  const data = msg.data;
@@ -792,7 +792,7 @@ const converters = {
792
792
  },
793
793
  },
794
794
  light_onoff_brightness: {
795
- key: ['state', 'brightness', 'brightness_percent'],
795
+ key: ['state', 'brightness', 'brightness_percent', 'on_time'],
796
796
  options: [exposes.options.transition()],
797
797
  convertSet: async (entity, key, value, meta) => {
798
798
  const {message} = meta;
@@ -2439,6 +2439,20 @@ const converters = {
2439
2439
  } else {
2440
2440
  await entity.command('closuresWindowCovering', 'stop', {}, utils.getOptions(meta.mapped, entity));
2441
2441
  }
2442
+
2443
+ if (!['ZNCLDJ11LM', 'ZNJLBL01LM'].includes(meta.mapped.model)) {
2444
+ // The code below is originally added for ZNCLDJ11LM (Koenkk/zigbee2mqtt#4585).
2445
+ // However, in Koenkk/zigbee-herdsman-converters#4039 it was replaced by reading
2446
+ // directly from currentPositionLiftPercentage, so that device is excluded.
2447
+ // For ZNJLBL01LM, in Koenkk/zigbee-herdsman-converters#4163 the position is read
2448
+ // through onEvent each time the motor stops, so it becomes redundant, and the
2449
+ // device is excluded.
2450
+ // The code is left here to avoid breaking compatibility, ideally all devices using
2451
+ // this converter should be tested so the code can be adjusted/deleted.
2452
+
2453
+ // Xiaomi curtain does not send position update on stop, request this.
2454
+ await entity.read('genAnalogOutput', [0x0055]);
2455
+ }
2442
2456
  } else {
2443
2457
  const lookup = {'open': 100, 'close': 0, 'on': 100, 'off': 0};
2444
2458
 
@@ -5517,7 +5531,7 @@ const converters = {
5517
5531
  },
5518
5532
  },
5519
5533
  neo_nas_pd07: {
5520
- key: ['temperature_max', 'temperature_min', 'humidity_max', 'humidity_min'],
5534
+ key: ['temperature_max', 'temperature_min', 'humidity_max', 'humidity_min', 'temperature_scale', 'unknown_111', 'unknown_112'],
5521
5535
  convertSet: async (entity, key, value, meta) => {
5522
5536
  switch (key) {
5523
5537
  case 'temperature_max':
@@ -5532,8 +5546,17 @@ const converters = {
5532
5546
  case 'humidity_min':
5533
5547
  await tuya.sendDataPointValue(entity, tuya.dataPoints.neoMinHumidity, value);
5534
5548
  break;
5549
+ case 'temperature_scale':
5550
+ await tuya.sendDataPointBool(entity, tuya.dataPoints.neoTempScale, value === '°C');
5551
+ break;
5552
+ case 'unknown_111':
5553
+ await tuya.sendDataPointBool(entity, 111, value === 'ON');
5554
+ break;
5555
+ case 'unknown_112':
5556
+ await tuya.sendDataPointBool(entity, 112, value === 'ON');
5557
+ break;
5535
5558
  default: // Unknown key
5536
- throw new Error(`toZigbee.neo_nas_pd07: Unhandled key ${key}`);
5559
+ throw new Error(`tz.neo_nas_pd07: Unhandled key ${key}`);
5537
5560
  }
5538
5561
  },
5539
5562
  },
@@ -5579,7 +5602,7 @@ const converters = {
5579
5602
  await tuya.sendDataPointBool(entity, tuya.dataPoints.neoHumidityAlarm, value);
5580
5603
  break;
5581
5604
  default: // Unknown key
5582
- throw new Error(`Unhandled key ${key}`);
5605
+ throw new Error(`tz.neo_t_h_alarm: Unhandled key ${key}`);
5583
5606
  }
5584
5607
  },
5585
5608
  },
package/devices/awox.js CHANGED
@@ -224,11 +224,30 @@ module.exports = [
224
224
  },
225
225
  ],
226
226
  },
227
+ {
228
+ type: 'Router', manufacturerName: 'AwoX', modelID: 'TLSR82xx', endpoints: [
229
+ {
230
+ ID: 1,
231
+ profileID: 260,
232
+ deviceID: 268,
233
+ inputClusters: [0, 3, 4, 5, 6, 8, 768, 4096, 64599, 10],
234
+ outputClusters: [6],
235
+ },
236
+ {ID: 242, profileID: 41440, deviceID: 97, inputClusters: [], outputClusters: [33]},
237
+ {
238
+ ID: 3,
239
+ profileID: 4751,
240
+ deviceID: 268,
241
+ inputClusters: [65360, 65361],
242
+ outputClusters: [65360, 65361],
243
+ },
244
+ ],
245
+ },
227
246
  ],
228
247
  model: '33957',
229
248
  vendor: 'AwoX',
230
249
  description: 'LED light with color temperature',
231
- extend: extend.light_onoff_brightness_colortemp(),
250
+ extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 370]}),
232
251
  },
233
252
  {
234
253
  fingerprint: [
@@ -79,7 +79,8 @@ module.exports = [
79
79
  .withDescription('Mean radiator load for room calculated by gateway for load balancing purposes (-8000=undefined)')
80
80
  .withValueMin(-8000).withValueMax(2000),
81
81
  exposes.numeric('load_estimate', ea.STATE_GET)
82
- .withDescription('Load estimate on this radiator'),
82
+ .withDescription('Load estimate on this radiator')
83
+ .withValueMin(-8000).withValueMax(3600),
83
84
  exposes.binary('preheat_status', ea.STATE_GET, true, false)
84
85
  .withDescription('Specific for pre-heat running in Zigbee Weekly Schedule mode'),
85
86
  exposes.enum('adaptation_run_status', ea.STATE_GET, ['none', 'in_progress', 'found', 'lost'])
@@ -1,4 +1,5 @@
1
1
  const exposes = require('../lib/exposes');
2
+ const fz = require('../converters/fromZigbee');
2
3
  const tz = require('../converters/toZigbee');
3
4
  const e = exposes.presets;
4
5
  const ea = exposes.access;
@@ -47,4 +48,28 @@ module.exports = [
47
48
  vendor: 'Miboxer',
48
49
  extend: extend.light_onoff_brightness_colortemp_color({disableColorTempStartup: true, colorTempRange: [153, 500]}),
49
50
  },
51
+ {
52
+ fingerprint: [{manufacturerName: '_TZ3000_xwh1e22x'}],
53
+ model: 'FUT089Z',
54
+ vendor: 'MiBoxer',
55
+ description: 'RGB+CCT Remote',
56
+ fromZigbee: [fz.battery],
57
+ toZigbee: [],
58
+ exposes: [e.battery(), e.battery_voltage()],
59
+ configure: async (device, coordinatorEndpoint, logger) => {
60
+ const endpoint = device.getEndpoint(1);
61
+ await endpoint.read('genBasic', ['manufacturerName', 'zclVersion', 'appVersion', 'modelId', 'powerSource', 0xfffe]);
62
+ await endpoint.command('genGroups', 'miboxerSetZones', {zones: [
63
+ {zoneNum: 1, groupId: 101},
64
+ {zoneNum: 2, groupId: 102},
65
+ {zoneNum: 3, groupId: 103},
66
+ {zoneNum: 4, groupId: 104},
67
+ {zoneNum: 5, groupId: 105},
68
+ {zoneNum: 6, groupId: 106},
69
+ {zoneNum: 7, groupId: 107},
70
+ {zoneNum: 8, groupId: 108},
71
+ ]});
72
+ await endpoint.command('genBasic', 'tuyaSetup', {}, {disableDefaultResponse: true});
73
+ },
74
+ },
50
75
  ];
package/devices/neo.js CHANGED
@@ -12,18 +12,18 @@ module.exports = [
12
12
  model: 'NAS-AB02B0',
13
13
  vendor: 'Neo',
14
14
  description: 'Temperature & humidity sensor and alarm',
15
- fromZigbee: [fz.neo_t_h_alarm, fz.ignore_basic_report],
15
+ fromZigbee: [fz.neo_t_h_alarm, fz.ignore_basic_report, fz.ignore_tuya_set_time],
16
16
  toZigbee: [tz.neo_t_h_alarm],
17
17
  exposes: [
18
18
  e.temperature(), e.humidity(), exposes.binary('humidity_alarm', ea.STATE_SET, true, false), e.battery_low(),
19
19
  exposes.binary('temperature_alarm', ea.STATE_SET, true, false),
20
20
  exposes.binary('alarm', ea.STATE_SET, true, false),
21
21
  exposes.enum('melody', ea.STATE_SET, Array.from(Array(18).keys()).map((x)=>(x+1).toString())),
22
- exposes.numeric('duration', ea.STATE_SET).withUnit('second').withValueMin(0).withValueMax(1000),
23
- exposes.numeric('temperature_min', ea.STATE_SET).withUnit('°C').withValueMin(-10).withValueMax(35),
24
- exposes.numeric('temperature_max', ea.STATE_SET).withUnit('°C').withValueMin(-10).withValueMax(35),
25
- exposes.numeric('humidity_min', ea.STATE_SET).withUnit('%').withValueMin(0).withValueMax(100),
26
- exposes.numeric('humidity_max', ea.STATE_SET).withUnit('%').withValueMin(0).withValueMax(100),
22
+ exposes.numeric('duration', ea.STATE_SET).withUnit('second').withValueMin(0).withValueMax(1800),
23
+ exposes.numeric('temperature_min', ea.STATE_SET).withUnit('°C').withValueMin(-20).withValueMax(80),
24
+ exposes.numeric('temperature_max', ea.STATE_SET).withUnit('°C').withValueMin(-20).withValueMax(80),
25
+ exposes.numeric('humidity_min', ea.STATE_SET).withUnit('%').withValueMin(1).withValueMax(100),
26
+ exposes.numeric('humidity_max', ea.STATE_SET).withUnit('%').withValueMin(1).withValueMax(100),
27
27
  exposes.enum('volume', ea.STATE_SET, ['low', 'medium', 'high']),
28
28
  exposes.enum('power_type', ea.STATE, ['battery_full', 'battery_high', 'battery_medium', 'battery_low', 'usb']),
29
29
  ],
@@ -62,16 +62,25 @@ module.exports = [
62
62
  model: 'NAS-PD07',
63
63
  vendor: 'Neo',
64
64
  description: 'Motion, temperature & humidity sensor',
65
- fromZigbee: [fz.neo_nas_pd07],
65
+ fromZigbee: [fz.neo_nas_pd07, fz.ignore_tuya_set_time],
66
66
  toZigbee: [tz.neo_nas_pd07],
67
67
  onEvent: tuya.onEventSetTime,
68
68
  exposes: [e.occupancy(), e.humidity(), e.temperature(), e.tamper(), e.battery_low(),
69
69
  exposes.enum('power_type', ea.STATE, ['battery_full', 'battery_high', 'battery_medium', 'battery_low', 'usb']),
70
70
  exposes.enum('alarm', ea.STATE, ['over_temperature', 'over_humidity', 'below_min_temperature', 'below_min_humdity', 'off'])
71
71
  .withDescription('Temperature/humidity alarm status'),
72
- exposes.numeric('temperature_min', ea.STATE_SET).withUnit('°C').withValueMin(-40).withValueMax(40),
73
- exposes.numeric('temperature_max', ea.STATE_SET).withUnit('°C').withValueMin(-40).withValueMax(40),
74
- exposes.numeric('humidity_min', ea.STATE_SET).withUnit('%').withValueMin(0).withValueMax(100),
75
- exposes.numeric('humidity_max', ea.STATE_SET).withUnit('%').withValueMin(0).withValueMax(100)],
72
+ exposes.numeric('temperature_min', ea.STATE_SET).withUnit('°C').withValueMin(-20).withValueMax(80),
73
+ exposes.numeric('temperature_max', ea.STATE_SET).withUnit('°C').withValueMin(-20).withValueMax(80),
74
+ exposes.binary('temperature_scale', ea.STATE_SET, '°C', '°F').withDescription('Temperature scale (°F/°C)'),
75
+ exposes.numeric('humidity_min', ea.STATE_SET).withUnit('%').withValueMin(1).withValueMax(100),
76
+ exposes.numeric('humidity_max', ea.STATE_SET).withUnit('%').withValueMin(1).withValueMax(100),
77
+ // exposes.binary('unknown_111', ea.STATE_SET, 'ON', 'OFF').withDescription('Unknown datapoint 111 (default: ON)'),
78
+ // exposes.binary('unknown_112', ea.STATE_SET, 'ON', 'OFF').withDescription('Unknown datapoint 112 (default: ON)')
79
+ ],
80
+ configure: async (device, coordinatorEndpoint, logger) => {
81
+ const endpoint = device.getEndpoint(1);
82
+ await endpoint.read('genBasic', ['manufacturerName', 'zclVersion', 'appVersion', 'modelId', 'powerSource', 0xfffe]);
83
+ await endpoint.command('manuSpecificTuya', 'mcuVersionRequest', {'seq': 0x0002});
84
+ },
76
85
  },
77
86
  ];
package/devices/nue_3a.js CHANGED
@@ -137,8 +137,12 @@ module.exports = [
137
137
  model: 'HGZB-04D / HGZB-4D-UK',
138
138
  vendor: 'Nue / 3A',
139
139
  description: 'Smart dimmer wall switch',
140
- extend: extend.light_onoff_brightness({disableEffect: true}),
140
+ extend: extend.light_onoff_brightness({disableEffect: true, noConfigure: true}),
141
141
  whiteLabel: [{vendor: 'Sunricher', model: 'SR-ZG9001K8-DIM'}],
142
+ configure: async (device, coordinatorEndpoint, logger) => {
143
+ await extend.light_onoff_brightness().configure(device, coordinatorEndpoint, logger);
144
+ await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff', 'genLevelCtrl']);
145
+ },
142
146
  },
143
147
  {
144
148
  zigbeeModel: ['FB56+ZSW1HKJ1.7', 'FB56+ZSW1HKJ2.5', 'FB56+ZSW1HKJ2.7'],
@@ -282,7 +286,7 @@ module.exports = [
282
286
  extend: extend.light_onoff_brightness_colortemp_color(),
283
287
  },
284
288
  {
285
- zigbeeModel: ['LXN60-LS27-Z30'],
289
+ zigbeeModel: ['LXN60-LS27-Z30', 'FEB56-ZCW2CLX1.0'],
286
290
  model: 'WL-SD001-9W',
287
291
  vendor: 'Nue / 3A',
288
292
  description: '9W RGB LED downlight',
@@ -108,6 +108,15 @@ module.exports = [
108
108
  extend: hueExtend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
109
109
  ota: ota.zigbeeOTA,
110
110
  },
111
+ {
112
+ zigbeeModel: ['915005996801', '915005996901'],
113
+ model: '915005996901',
114
+ vendor: 'Philips',
115
+ description: 'Hue white ambiance ceiling light Enrave L with Bluetooth',
116
+ meta: {turnsOffAtBrightness1: true},
117
+ extend: hueExtend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
118
+ ota: ota.zigbeeOTA,
119
+ },
111
120
  {
112
121
  zigbeeModel: ['915005997001', '915005997101'],
113
122
  model: '915005997001',
package/devices/tuya.js CHANGED
@@ -13,7 +13,7 @@ const utils = require('../lib/utils');
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
15
  '_TZ3000_rdtixbnu', '_TZ3000_typdpbpg', '_TZ3000_kx0pris5', '_TZ3000_amdymr7l', '_TZ3000_z1pnpsdo', '_TZ3000_ksw8qtmt',
16
- '_TZ3000_nzkqcvvs', '_TZ3000_1h2x4akh'];
16
+ '_TZ3000_nzkqcvvs', '_TZ3000_1h2x4akh', '_TZ3000_9vo5icau'];
17
17
 
18
18
  const tzLocal = {
19
19
  TS0504B_color: {
@@ -167,7 +167,7 @@ module.exports = [
167
167
  description: 'Air quality sensor',
168
168
  fromZigbee: [fz.tuya_air_quality],
169
169
  toZigbee: [],
170
- exposes: [e.temperature(), e.humidity(), e.co2(), e.voc(), e.formaldehyd()],
170
+ exposes: [e.temperature(), e.humidity(), e.co2(), e.voc().withUnit('ppm'), e.formaldehyd()],
171
171
  },
172
172
  {
173
173
  fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_dwcarsat'}],
@@ -1086,7 +1086,6 @@ module.exports = [
1086
1086
  // Window pushers:
1087
1087
  {modelID: 'TS0601', manufacturerName: '_TZE200_g5wdnuow'},
1088
1088
  // Tubular motors:
1089
- {modelID: 'TS0601', manufacturerName: '_TZE200_fzo2pocs'},
1090
1089
  {modelID: 'TS0601', manufacturerName: '_TZE200_5sbebbzs'},
1091
1090
  {modelID: 'TS0601', manufacturerName: '_TZE200_zuz7f94z'},
1092
1091
  {modelID: 'TS0601', manufacturerName: '_TZE200_zyrdrmno'},
@@ -1107,7 +1106,6 @@ module.exports = [
1107
1106
  {vendor: 'Moes', model: 'AM43-0.45/40-ES-EB'},
1108
1107
  {vendor: 'Larkkey', model: 'ZSTY-SM-1SRZG-EU'},
1109
1108
  {vendor: 'Zemismart', model: 'ZM85EL-2Z', description: 'Roman Rod I type curtains track'},
1110
- {vendor: 'Zemismart', model: 'ZM25TQ', description: 'Tubular motor'},
1111
1109
  {vendor: 'Zemismart', model: 'AM43', description: 'Roller blind motor'},
1112
1110
  {vendor: 'Zemismart', model: 'M2805EGBZTN', description: 'Tubular motor'},
1113
1111
  {vendor: 'Zemismart', model: 'BCM500DS-TYZ', description: 'Curtain motor'},
@@ -1415,7 +1413,8 @@ module.exports = [
1415
1413
  model: 'TS011F_plug_3',
1416
1414
  description: 'Smart plug (with power monitoring by polling)',
1417
1415
  vendor: 'TuYa',
1418
- whiteLabel: [{vendor: 'VIKEFON', model: 'TS011F'}, {vendor: 'BlitzWolf', model: 'BW-SHP15'}],
1416
+ whiteLabel: [{vendor: 'VIKEFON', model: 'TS011F'}, {vendor: 'BlitzWolf', model: 'BW-SHP15'},
1417
+ {vendor: 'Avatto', model: 'MIUCOT10Z'}],
1419
1418
  ota: ota.zigbeeOTA,
1420
1419
  fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering, fz.ignore_basic_report, fz.tuya_switch_power_outage_memory,
1421
1420
  fz.ts011f_plug_indicator_mode, fz.ts011f_plug_child_mode],
package/devices/xiaomi.js CHANGED
@@ -844,15 +844,17 @@ module.exports = [
844
844
  model: 'WSDCGQ12LM',
845
845
  vendor: 'Xiaomi',
846
846
  description: 'Aqara T1 temperature, humidity and pressure sensor',
847
- fromZigbee: [fz.xiaomi_basic, fz.temperature, fz.humidity, fz.pressure],
847
+ fromZigbee: [fz.aqara_opple, fz.temperature, fz.humidity, fz.pressure, fz.battery],
848
848
  toZigbee: [],
849
+ exposes: [e.temperature(), e.humidity(), e.pressure(), e.battery(), e.battery_voltage()],
849
850
  meta: {battery: {voltageToPercentage: '3V_2850_3000_log'}},
850
851
  configure: async (device, coordinatorEndpoint, logger) => {
851
852
  const endpoint = device.getEndpoint(1);
852
853
  const binds = ['msTemperatureMeasurement', 'msRelativeHumidity', 'msPressureMeasurement'];
853
854
  await reporting.bind(endpoint, coordinatorEndpoint, binds);
855
+ await endpoint.read('genPowerCfg', ['batteryVoltage']);
854
856
  },
855
- exposes: [e.battery(), e.temperature(), e.humidity(), e.pressure(), e.battery_voltage()],
857
+ ota: ota.zigbeeOTA,
856
858
  },
857
859
  {
858
860
  zigbeeModel: ['lumi.sensor_motion'],
@@ -1348,9 +1350,16 @@ module.exports = [
1348
1350
  model: 'ZNJLBL01LM',
1349
1351
  description: 'Aqara roller shade companion E1',
1350
1352
  vendor: 'Xiaomi',
1351
- fromZigbee: [fz.xiaomi_curtain_acn002_position, fz.xiaomi_curtain_acn002_status, fz.xiaomi_curtain_position_tilt,
1352
- fz.ignore_basic_report, fz.aqara_opple],
1353
+ fromZigbee: [fz.xiaomi_curtain_position, fz.xiaomi_curtain_acn002_status, fz.ignore_basic_report, fz.aqara_opple],
1353
1354
  toZigbee: [tz.xiaomi_curtain_position_state, tz.xiaomi_curtain_acn002_battery, tz.xiaomi_curtain_acn002_charging_status],
1355
+ onEvent: async (type, data, device) => {
1356
+ if (type === 'message' && data.type === 'attributeReport' && data.cluster === 'genMultistateOutput' &&
1357
+ data.data.hasOwnProperty('presentValue') && data.data['presentValue'] > 1) {
1358
+ // Try to read the position after the motor stops, the device occasionally report wrong data right after stopping
1359
+ // Might need to add delay, seems to be working without one but needs more tests.
1360
+ await device.getEndpoint(1).read('genAnalogOutput', ['presentValue']);
1361
+ }
1362
+ },
1354
1363
  exposes: [e.cover_position().setAccess('state', ea.ALL), e.battery().withAccess(ea.STATE_GET),
1355
1364
  exposes.binary('charging_status', ea.STATE_GET, true, false)
1356
1365
  .withDescription('The current charging status.'),
@@ -154,4 +154,13 @@ module.exports = [
154
154
  // exposes.enum('situation_set', ea.STATE, Object.values(tuya.ZMAM02.AM02Situation)),
155
155
  ],
156
156
  },
157
+ {
158
+ fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_fzo2pocs'}],
159
+ model: 'ZM25TQ',
160
+ vendor: 'Zemismart',
161
+ description: 'Tubular motor',
162
+ fromZigbee: [fz.tuya_cover, fz.ignore_basic_report],
163
+ toZigbee: [tz.tuya_cover_control, tz.tuya_cover_options, tz.tuya_data_point_test],
164
+ exposes: [e.cover_position().setAccess('position', ea.STATE_SET)],
165
+ },
157
166
  ];
package/lib/exposes.js CHANGED
@@ -550,7 +550,7 @@ module.exports = {
550
550
  fan: () => new Fan(),
551
551
  flip_indicator_light: () => new Binary('flip_indicator_light', access.ALL, 'ON', 'OFF').withDescription('After turn on, the indicator light turns on while switch is off, and vice versa'),
552
552
  force: () => new Enum('force', access.STATE_SET, ['normal', 'open', 'close']).withDescription('Force the valve position'),
553
- formaldehyd: () => new Numeric('formaldehyd', access.STATE).withDescription('The measured formaldehyd value'),
553
+ formaldehyd: () => new Numeric('formaldehyd', access.STATE).withDescription('The measured formaldehyd value').withUnit('mg/m³'),
554
554
  gas: () => new Binary('gas', access.STATE, true, false).withDescription('Indicates whether the device detected gas'),
555
555
  hcho: () => new Numeric('hcho', access.STATE).withUnit('mg/m³').withDescription('Measured Hcho value'),
556
556
  holiday_temperature: () => new Numeric('holiday_temperature', access.STATE_SET).withUnit('°C').withDescription('Holiday temperature').withValueMin(0).withValueMax(30),
package/lib/tuya.js CHANGED
@@ -358,11 +358,14 @@ const dataPoints = {
358
358
  moesSmaxTempSet: 108,
359
359
  moesSminTempSet: 109,
360
360
  // Neo T&H
361
+ neoOccupancy: 101,
361
362
  neoPowerType: 101,
362
363
  neoMelody: 102,
363
364
  neoDuration: 103,
365
+ neoTamper: 103,
364
366
  neoAlarm: 104,
365
367
  neoTemp: 105,
368
+ neoTempScale: 106,
366
369
  neoHumidity: 106,
367
370
  neoMinTemp: 107,
368
371
  neoMaxTemp: 108,
@@ -370,6 +373,7 @@ const dataPoints = {
370
373
  neoMaxHumidity: 110,
371
374
  neoUnknown2: 112,
372
375
  neoTempAlarm: 113,
376
+ neoTempHumidityAlarm: 113,
373
377
  neoHumidityAlarm: 114,
374
378
  neoUnknown3: 115,
375
379
  neoVolume: 116,
package/lib/xiaomi.js CHANGED
@@ -158,7 +158,9 @@ const numericAttributes2Payload = (msg, meta, model, options, dataObject) => {
158
158
  }
159
159
  break;
160
160
  case '3':
161
- if (!['WXCJKG11LM', 'WXCJKG12LM', 'WXCJKG13LM'].includes(model.model)) {
161
+ if (['WSDCGQ12LM'].includes(model.model)) {
162
+ // This temperature value is ignored because with greater accuracy it is reported in key №100
163
+ } else if (!['WXCJKG11LM', 'WXCJKG12LM', 'WXCJKG13LM'].includes(model.model)) {
162
164
  payload.temperature = calibrateAndPrecisionRoundOptions(value, options, 'temperature'); // 0x03
163
165
  }
164
166
  break;
@@ -196,10 +198,11 @@ const numericAttributes2Payload = (msg, meta, model, options, dataObject) => {
196
198
  payload[`state_${mapping}`] = value === 1 ? 'ON' : 'OFF';
197
199
  } else if (['WXKG14LM', 'WXKG16LM', 'WXKG17LM'].includes(model.model)) {
198
200
  payload.click_mode = {1: 'fast', 2: 'multi'}[value];
199
- } else if (['WXCJKG11LM', 'WXCJKG12LM', 'WXCJKG13LM'].includes(model.model)) {
201
+ } else if (['WXCJKG11LM', 'WXCJKG12LM', 'WXCJKG13LM', 'ZNMS12LM'].includes(model.model)) {
200
202
  // We don't know what the value means for these devices.
201
203
  // https://github.com/Koenkk/zigbee2mqtt/issues/11126
202
- } else if (['WSDCGQ01LM', 'WSDCGQ11LM'].includes(model.model)) {
204
+ // https://github.com/Koenkk/zigbee2mqtt/issues/12279
205
+ } else if (['WSDCGQ01LM', 'WSDCGQ11LM', 'WSDCGQ12LM'].includes(model.model)) {
203
206
  // https://github.com/Koenkk/zigbee2mqtt/issues/798
204
207
  // Sometimes the sensor publishes non-realistic vales, filter these
205
208
  const temperature = parseFloat(value) / 100.0;
@@ -241,7 +244,7 @@ const numericAttributes2Payload = (msg, meta, model, options, dataObject) => {
241
244
  payload[`state_${mapping}`] = value === 1 ? 'ON' : 'OFF';
242
245
  } else if (['RTCGQ12LM', 'RTCGQ14LM'].includes(model.model)) {
243
246
  payload.illuminance = calibrateAndPrecisionRoundOptions(value, options, 'illuminance');
244
- } else if (['WSDCGQ01LM', 'WSDCGQ11LM'].includes(model.model)) {
247
+ } else if (['WSDCGQ01LM', 'WSDCGQ11LM', 'WSDCGQ12LM'].includes(model.model)) {
245
248
  // https://github.com/Koenkk/zigbee2mqtt/issues/798
246
249
  // Sometimes the sensor publishes non-realistic vales, filter these
247
250
  const humidity = parseFloat(value) / 100.0;
@@ -259,6 +262,9 @@ const numericAttributes2Payload = (msg, meta, model, options, dataObject) => {
259
262
  payload.state_right = value === 1 ? 'ON' : 'OFF';
260
263
  } else if (['WSDCGQ01LM', 'WSDCGQ11LM'].includes(model.model)) {
261
264
  payload.pressure = calibrateAndPrecisionRoundOptions(value/100.0, options, 'pressure');
265
+ } else if (['WSDCGQ12LM'].includes(model.model)) {
266
+ // This pressure value is ignored because it is less accurate than reported in the 'scaledValue' attribute
267
+ // of the 'msPressureMeasurement' cluster
262
268
  } else if (['RTCZCGQ11LM'].includes(model.model)) {
263
269
  if (meta.device.applicationVersion < 50) {
264
270
  payload.presence_event = {0: 'enter', 1: 'leave', 2: 'left_enter', 3: 'right_leave', 4: 'right_enter',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "14.0.498",
3
+ "version": "14.0.501",
4
4
  "description": "Collection of device converters to be used with zigbee-herdsman",
5
5
  "main": "index.js",
6
6
  "files": [