zigbee-herdsman-converters 14.0.334 → 14.0.338

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.
@@ -1757,6 +1757,29 @@ const converters = {
1757
1757
  }
1758
1758
  },
1759
1759
  },
1760
+ tuya_illuminance_temperature_humidity_sensor: {
1761
+ cluster: 'manuSpecificTuya',
1762
+ type: ['commandSetDataResponse', 'commandGetData'],
1763
+ options: [exposes.options.precision('temperature'), exposes.options.calibration('temperature'),
1764
+ exposes.options.precision('humidity'), exposes.options.calibration('humidity')],
1765
+ convert: (model, msg, publish, options, meta) => {
1766
+ const dp = msg.data.dp;
1767
+ const value = tuya.getDataValue(msg.data.datatype, msg.data.data);
1768
+ switch (dp) {
1769
+ case tuya.dataPoints.thitTemperature:
1770
+ return {temperature: calibrateAndPrecisionRoundOptions(value / 10, options, 'temperature')};
1771
+ case tuya.dataPoints.thitHumidity:
1772
+ return {humidity: calibrateAndPrecisionRoundOptions(value, options, 'humidity')};
1773
+ case tuya.dataPoints.thitBatteryPercentage:
1774
+ return {battery: value};
1775
+ case tuya.dataPoints.thitIlluminanceLux:
1776
+ return {illuminance_lux: value};
1777
+ default:
1778
+ meta.logger.warn(`zigbee-herdsman-converters:tuya_illuminance_temperature_humidity_sensor: NOT RECOGNIZED ` +
1779
+ `DP #${dp} with data ${JSON.stringify(msg.data)}`);
1780
+ }
1781
+ },
1782
+ },
1760
1783
  ts0201_temperature_humidity_alarm: {
1761
1784
  cluster: 'manuSpecificTuya_2',
1762
1785
  type: ['attributeReport', 'readResponse'],
@@ -2295,20 +2318,20 @@ const converters = {
2295
2318
  exposes.options.precision('humidity'), exposes.options.calibration('humidity')],
2296
2319
  convert: (model, msg, publish, options, meta) => {
2297
2320
  const dp = msg.data.dp;
2298
- if (dp === 101) return {occupancy: msg.data.data[0] > 0};
2321
+ const value = tuya.getDataValue(msg.data.datatype, msg.data.data);
2322
+
2323
+ if (dp === 101) return {occupancy: value > 0 ? true : false};
2299
2324
  else if (dp === 102) {
2300
- const value = msg.data.data[0];
2301
2325
  return {
2302
2326
  power_type: {0: 'battery_full', 1: 'battery_high', 2: 'battery_medium', 3: 'battery_low', 4: 'usb'}[value],
2303
2327
  battery_low: value === 3,
2304
2328
  };
2305
2329
  } else if (dp === 103) {
2306
- return {tamper: msg.data.data[0] > 0 ? true : false};
2330
+ return {tamper: value > 0 ? true : false};
2307
2331
  } else if (dp === 104) {
2308
- const temperature = parseFloat(msg.data.data[3]) / 10.0;
2309
- return {temperature: calibrateAndPrecisionRoundOptions(temperature, options, 'temperature')};
2332
+ return {temperature: calibrateAndPrecisionRoundOptions(value / 10, options, 'temperature')};
2310
2333
  } else if (dp === 105) {
2311
- return {humidity: calibrateAndPrecisionRoundOptions(msg.data.data[3], options, 'humidity')};
2334
+ return {humidity: calibrateAndPrecisionRoundOptions(value, options, 'humidity')};
2312
2335
  } else {
2313
2336
  meta.logger.warn(`zigbee-herdsman-converters:NEO-PD07: NOT RECOGNIZED DP #${dp} with data ${JSON.stringify(msg.data)}`);
2314
2337
  }
@@ -2448,7 +2471,7 @@ const converters = {
2448
2471
  buttonMapping = {1: '1', 2: '2'};
2449
2472
  } else if (model.model === 'TS0043') {
2450
2473
  buttonMapping = {1: '1', 2: '2', 3: '3'};
2451
- } else if (['TS0044', 'YSR-MINI-Z'].includes(model.model)) {
2474
+ } else if (['TS0044', 'YSR-MINI-Z', 'TS004F'].includes(model.model)) {
2452
2475
  buttonMapping = {1: '1', 2: '2', 3: '3', 4: '4'};
2453
2476
  }
2454
2477
  const button = buttonMapping ? `${buttonMapping[msg.endpoint.ID]}_` : '';
@@ -7487,6 +7510,44 @@ const converters = {
7487
7510
  }
7488
7511
  },
7489
7512
  },
7513
+ sunricher_switch2801K2: {
7514
+ cluster: 'greenPower',
7515
+ type: ['commandNotification', 'commandCommisioningNotification'],
7516
+ convert: (model, msg, publish, options, meta) => {
7517
+ const commandID = msg.data.commandID;
7518
+ if (hasAlreadyProcessedMessage(msg, msg.data.frameCounter, `${msg.device.ieeeAddr}_${commandID}`)) return;
7519
+ if (commandID === 224) return;
7520
+ const lookup = {0x21: 'press_on', 0x20: 'press_off', 0x34: 'release', 0x35: 'hold_on', 0x36: 'hold_off'};
7521
+ if (!lookup.hasOwnProperty(commandID)) {
7522
+ meta.logger.error(`Sunricher: missing command '${commandID}'`);
7523
+ } else {
7524
+ return {action: lookup[commandID]};
7525
+ }
7526
+ },
7527
+ },
7528
+ sunricher_switch2801K4: {
7529
+ cluster: 'greenPower',
7530
+ type: ['commandNotification', 'commandCommisioningNotification'],
7531
+ convert: (model, msg, publish, options, meta) => {
7532
+ const commandID = msg.data.commandID;
7533
+ if (hasAlreadyProcessedMessage(msg, msg.data.frameCounter, `${msg.device.ieeeAddr}_${commandID}`)) return;
7534
+ if (commandID === 224) return;
7535
+ const lookup = {
7536
+ 0x21: 'press_on',
7537
+ 0x20: 'press_off',
7538
+ 0x37: 'press_high',
7539
+ 0x38: 'press_low',
7540
+ 0x35: 'hold_high',
7541
+ 0x36: 'hold_low',
7542
+ 0x34: 'release',
7543
+ };
7544
+ if (!lookup.hasOwnProperty(commandID)) {
7545
+ meta.logger.error(`Sunricher: missing command '${commandID}'`);
7546
+ } else {
7547
+ return {action: lookup[commandID]};
7548
+ }
7549
+ },
7550
+ },
7490
7551
  // #endregion
7491
7552
 
7492
7553
  // #region Ignore converters (these message dont need parsing).
@@ -0,0 +1,34 @@
1
+ const exposes = require('../lib/exposes');
2
+ const fz = require('../converters/fromZigbee');
3
+ const tz = require('../converters/toZigbee');
4
+ const reporting = require('../lib/reporting');
5
+
6
+ module.exports = [{
7
+ zigbeeModel: ['PERCALE2 D1.00P1.01Z1.00'],
8
+ model: 'PERCALE2',
9
+ vendor: 'Acova',
10
+ description: 'Percale 2 heater',
11
+ fromZigbee: [fz.thermostat, fz.hvac_user_interface],
12
+ toZigbee: [
13
+ tz.thermostat_local_temperature,
14
+ tz.thermostat_system_mode,
15
+ tz.thermostat_occupied_heating_setpoint,
16
+ tz.thermostat_unoccupied_heating_setpoint,
17
+ tz.thermostat_occupied_cooling_setpoint,
18
+ tz.thermostat_running_state,
19
+ ],
20
+ exposes: [
21
+ exposes.climate()
22
+ .withSetpoint('occupied_heating_setpoint', 7, 28, 0.5)
23
+ .withLocalTemperature()
24
+ .withSystemMode(['off', 'heat', 'auto'])
25
+ .withRunningState(['idle', 'heat']),
26
+ ],
27
+ configure: async (device, coordinatorEndpoint, logger) => {
28
+ const endpoint = device.getEndpoint(1);
29
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'hvacThermostat']);
30
+ await reporting.thermostatTemperature(endpoint);
31
+ await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
32
+ await reporting.thermostatUnoccupiedHeatingSetpoint(endpoint);
33
+ },
34
+ }];
@@ -233,4 +233,40 @@ module.exports = [
233
233
  },
234
234
  exposes: [e.soil_moisture(), e.battery(), e.temperature()],
235
235
  },
236
+ {
237
+ zigbeeModel: ['EFEKTA_eON213z'],
238
+ model: 'EFEKTA_eON213z',
239
+ vendor: 'Custom devices (DiY)',
240
+ description: '[Temperature and humidity sensor with e-ink2.13](http://efektalab.com/eON213z)',
241
+ fromZigbee: [fz.temperature, fz.humidity, fz.battery],
242
+ toZigbee: [tz.factory_reset],
243
+ configure: async (device, coordinatorEndpoint, logger) => {
244
+ const endpoint = device.getEndpoint(1);
245
+ await reporting.bind(endpoint, coordinatorEndpoint, [
246
+ 'genPowerCfg', 'msTemperatureMeasurement', 'msRelativeHumidity']);
247
+ const overides = {min: 0, max: 21600, change: 0};
248
+ await reporting.batteryVoltage(endpoint, overides);
249
+ await reporting.batteryPercentageRemaining(endpoint, overides);
250
+ await reporting.temperature(endpoint, overides);
251
+ await reporting.humidity(endpoint, overides);
252
+ },
253
+ exposes: [e.battery(), e.temperature(), e.humidity()],
254
+ },
255
+ {
256
+ zigbeeModel: ['EFEKTA_miniPWS'],
257
+ model: 'EFEKTA_miniPWS',
258
+ vendor: 'Custom devices (DiY)',
259
+ description: '[Mini plant wattering sensor](http://efektalab.com/miniPWS)',
260
+ fromZigbee: [fz.soil_moisture, fz.battery],
261
+ toZigbee: [tz.factory_reset],
262
+ configure: async (device, coordinatorEndpoint, logger) => {
263
+ const firstEndpoint = device.getEndpoint(1);
264
+ await reporting.bind(firstEndpoint, coordinatorEndpoint, ['genPowerCfg', 'msSoilMoisture']);
265
+ const overides = {min: 0, max: 21600, change: 0};
266
+ await reporting.batteryVoltage(firstEndpoint, overides);
267
+ await reporting.batteryPercentageRemaining(firstEndpoint, overides);
268
+ await reporting.soil_moisture(firstEndpoint, overides);
269
+ },
270
+ exposes: [e.soil_moisture(), e.battery()],
271
+ },
236
272
  ];
@@ -34,7 +34,9 @@ module.exports = [
34
34
  exposes.binary('viewing_direction', ea.ALL, true, false)
35
35
  .withDescription('Viewing/Display Direction. `false` Horizontal or `true` Vertical'),
36
36
  exposes.binary('heat_available', ea.ALL, true, false)
37
- .withDescription('Not clear how this affects operation. `false` No Heat Available or `true` Heat Available'),
37
+ .withDescription('Not clear how this affects operation. However, it would appear that the device does not execute any ' +
38
+ 'motor functions if this is set to false. This may be a means to conserve battery during periods that the heating ' +
39
+ 'system is not energized (e.g. during summer). `false` No Heat Available or `true` Heat Available'),
38
40
  exposes.binary('heat_required', ea.STATE_GET, true, false)
39
41
  .withDescription('Whether or not the unit needs warm water. `false` No Heat Request or `true` Heat Request'),
40
42
  exposes.enum('setpoint_change_source', ea.STATE, ['manual', 'schedule', 'externally'])
@@ -42,8 +44,9 @@ module.exports = [
42
44
  exposes.climate().withSetpoint('occupied_heating_setpoint', 5, 32, 0.5).withLocalTemperature().withPiHeatingDemand()
43
45
  .withSystemMode(['heat']).withRunningState(['idle', 'heat'], ea.STATE),
44
46
  exposes.numeric('external_measured_room_sensor', ea.ALL)
45
- .withDescription('Set at maximum 3 hours interval but not more often than every 30 minutes at every 100 ' +
46
- 'value change. Resets every 3hours to standard. e.g. 21C = 2100 (-8000=undefined).'),
47
+ .withDescription('Set at maximum 3 hours interval but not more often than every 30 minutes and 0.1 ' +
48
+ 'degrees difference. Resets every 3hours to standard. e.g. 21C = 2100 (-8000=undefined).')
49
+ .withValueMin(-8000).withValueMax(3500),
47
50
  exposes.numeric('window_open_internal', ea.STATE_GET).withValueMin(0).withValueMax(4)
48
51
  .withDescription('0=Quarantine, 1=Windows are closed, 2=Hold - Windows are maybe about to open, ' +
49
52
  '3=Open window detected, 4=In window open state from external but detected closed locally'),
@@ -62,7 +65,8 @@ module.exports = [
62
65
  .withDescription('Whether or not the thermostat acts as standalone thermostat or shares load with other ' +
63
66
  'thermostats in the room. The gateway must update load_room_mean if enabled.'),
64
67
  exposes.numeric('load_room_mean', ea.ALL)
65
- .withDescription('Mean radiator load for room calculated by gateway for load balancing purposes'),
68
+ .withDescription('Mean radiator load for room calculated by gateway for load balancing purposes (-8000=undefined)')
69
+ .withValueMin(-8000).withValueMax(100),
66
70
  exposes.numeric('load_estimate', ea.STATE_GET)
67
71
  .withDescription('Load estimate on this radiator')],
68
72
  ota: ota.zigbeeOTA,
@@ -318,7 +318,7 @@ module.exports = [
318
318
  ['electricity', 'gas', 'water', 'kamstrup-kmp', 'linky', 'IEC62056-21', 'DSMR-2.3', 'DSMR-4.0'])
319
319
  .withDescription('Operating mode/probe'),
320
320
  exposes.numeric('current_summation', ea.SET)
321
- .withDescription('Current summation value sent to the display. e.g. 570 = 0,570 kWh'),
321
+ .withDescription('Current summation value sent to the display. e.g. 570 = 0,570 kWh').withValueMin(0).withValueMax(10000),
322
322
  exposes.binary('check_meter', ea.STATE, true, false)
323
323
  .withDescription('Is true if communication problem with meter is experienced'),
324
324
  ],
package/devices/diyruz.js CHANGED
@@ -150,11 +150,13 @@ module.exports = [
150
150
  exposes.numeric('radiation_dose_per_hour', ea.STATE).withUnit('μR/h').withDescription('Current radiation level'),
151
151
  exposes.binary('led_feedback', ea.ALL, 'ON', 'OFF').withDescription('Enable LED feedback'),
152
152
  exposes.binary('buzzer_feedback', ea.ALL, 'ON', 'OFF').withDescription('Enable buzzer feedback'),
153
- exposes.numeric('alert_threshold', ea.ALL).withUnit('μR/h').withDescription('Critical radiation level'),
153
+ exposes.numeric('alert_threshold', ea.ALL).withUnit('μR/h').withDescription('Critical radiation level')
154
+ .withValueMin(0).withValueMax(10000),
154
155
  exposes.enum('sensors_type', ea.ALL, ['СБМ-20/СТС-5/BOI-33', 'СБМ-19/СТС-6', 'Others'])
155
156
  .withDescription('Type of installed tubes'),
156
- exposes.numeric('sensors_count', ea.ALL).withDescription('Count of installed tubes'),
157
- exposes.numeric('sensitivity', ea.ALL).withDescription('This is applicable if tubes type is set to other')],
157
+ exposes.numeric('sensors_count', ea.ALL).withDescription('Count of installed tubes').withValueMin(0).withValueMax(50),
158
+ exposes.numeric('sensitivity', ea.ALL).withDescription('This is applicable if tubes type is set to other')
159
+ .withValueMin(0).withValueMax(100)],
158
160
  toZigbee: [tz.diyruz_geiger_config, tz.factory_reset],
159
161
  configure: async (device, coordinatorEndpoint, logger) => {
160
162
  const endpoint = device.getEndpoint(1);
@@ -250,11 +252,16 @@ module.exports = [
250
252
  exposes: [e.co2(), e.temperature(), e.humidity(), e.pressure(),
251
253
  exposes.binary('led_feedback', ea.ALL, 'ON', 'OFF').withDescription('Enable LEDs feedback'),
252
254
  exposes.binary('enable_abc', ea.ALL, 'ON', 'OFF').withDescription('Enable ABC (Automatic Baseline Correction)'),
253
- exposes.numeric('threshold1', ea.ALL).withUnit('ppm').withDescription('Warning (LED2) CO2 level'),
254
- exposes.numeric('threshold2', ea.ALL).withUnit('ppm').withDescription('Critical (LED3) CO2 level'),
255
- exposes.numeric('temperature_offset', ea.ALL).withUnit('°C').withDescription('Adjust temperature'),
256
- exposes.numeric('humidity_offset', ea.ALL).withUnit('%').withDescription('Adjust humidity'),
257
- exposes.numeric('pressure_offset', ea.ALL).withUnit('hPa').withDescription('Adjust pressure')],
255
+ exposes.numeric('threshold1', ea.ALL).withUnit('ppm').withDescription('Warning (LED2) CO2 level')
256
+ .withValueMin(0).withValueMax(50000),
257
+ exposes.numeric('threshold2', ea.ALL).withUnit('ppm').withDescription('Critical (LED3) CO2 level')
258
+ .withValueMin(0).withValueMax(50000),
259
+ exposes.numeric('temperature_offset', ea.ALL).withUnit('°C').withDescription('Adjust temperature')
260
+ .withValueMin(-20).withValueMax(20),
261
+ exposes.numeric('humidity_offset', ea.ALL).withUnit('%').withDescription('Adjust humidity')
262
+ .withValueMin(-50).withValueMax(50),
263
+ exposes.numeric('pressure_offset', ea.ALL).withUnit('hPa').withDescription('Adjust pressure')
264
+ .withValueMin(-1000).withValueMax(1000)],
258
265
  },
259
266
  {
260
267
  zigbeeModel: ['DIY_Zintercom'],
@@ -282,15 +289,15 @@ module.exports = [
282
289
  exposes.binary('sound', ea.ALL, 'ON', 'OFF').withProperty('sound')
283
290
  .withDescription('Enable or disable sound'),
284
291
  exposes.numeric('time_ring', ea.ALL).withUnit('sec')
285
- .withDescription('Time to ring before answer'),
292
+ .withDescription('Time to ring before answer').withValueMin(0).withValueMax(600),
286
293
  exposes.numeric('time_talk', ea.ALL).withUnit('sec')
287
- .withDescription('Time to hold before open'),
294
+ .withDescription('Time to hold before open').withValueMin(0).withValueMax(600),
288
295
  exposes.numeric('time_open', ea.ALL).withUnit('sec')
289
- .withDescription('Time to open before end'),
296
+ .withDescription('Time to open before end').withValueMin(0).withValueMax(600),
290
297
  exposes.numeric('time_bell', ea.ALL).withUnit('sec')
291
- .withDescription('Time after last bell to finish ring'),
298
+ .withDescription('Time after last bell to finish ring').withValueMin(0).withValueMax(600),
292
299
  exposes.numeric('time_report', ea.ALL).withUnit('min')
293
- .withDescription('Reporting interval'),
300
+ .withDescription('Reporting interval').withValueMin(0).withValueMax(1440),
294
301
  e.battery(),
295
302
  ],
296
303
  },
package/devices/fantem.js CHANGED
@@ -29,10 +29,14 @@ module.exports = [
29
29
  fromZigbee: [fz.battery, fz.ignore_basic_report, fz.illuminance, fz.ZB003X, fz.ZB003X_attr, fz.ZB003X_occupancy],
30
30
  toZigbee: [tz.ZB003X],
31
31
  exposes: [e.occupancy(), e.tamper(), e.battery(), e.illuminance(), e.illuminance_lux().withUnit('lx'), e.temperature(),
32
- e.humidity(), exposes.numeric('reporting_time', ea.STATE_SET).withDescription('Reporting interval in minutes'),
33
- exposes.numeric('temperature_calibration', ea.STATE_SET).withDescription('Temperature calibration'),
34
- exposes.numeric('humidity_calibration', ea.STATE_SET).withDescription('Humidity calibration'),
35
- exposes.numeric('illuminance_calibration', ea.STATE_SET).withDescription('Illuminance calibration'),
32
+ e.humidity(), exposes.numeric('reporting_time', ea.STATE_SET).withDescription('Reporting interval in minutes')
33
+ .withValueMin(0).withValueMax(1440),
34
+ exposes.numeric('temperature_calibration', ea.STATE_SET).withDescription('Temperature calibration')
35
+ .withValueMin(-20).withValueMax(20),
36
+ exposes.numeric('humidity_calibration', ea.STATE_SET).withDescription('Humidity calibration')
37
+ .withValueMin(-50).withValueMax(50),
38
+ exposes.numeric('illuminance_calibration', ea.STATE_SET).withDescription('Illuminance calibration')
39
+ .withValueMin(-10000).withValueMax(10000),
36
40
  exposes.binary('pir_enable', ea.STATE_SET, true, false).withDescription('Enable PIR sensor'),
37
41
  exposes.binary('led_enable', ea.STATE_SET, true, false).withDescription('Enabled LED'),
38
42
  exposes.binary('reporting_enable', ea.STATE_SET, true, false).withDescription('Enabled reporting'),
@@ -39,6 +39,27 @@ const gledoptoExtend = {
39
39
  [tz.gledopto_light_onoff_brightness, tz.gledopto_light_color_colortemp],
40
40
  ),
41
41
  }),
42
+ switch: (options={}) => ({
43
+ ...extend.switch(options),
44
+ onEvent: async (type, data, device) => {
45
+ // This device doesn't support reporting.
46
+ // Therefore we read the on/off state every 5 seconds.
47
+ // This is the same way as the Hue bridge does it.
48
+ if (type === 'stop') {
49
+ clearInterval(globalStore.getValue(device, 'interval'));
50
+ globalStore.clearValue(device, 'interval');
51
+ } else if (!globalStore.hasValue(device, 'interval')) {
52
+ const interval = setInterval(async () => {
53
+ try {
54
+ await device.endpoints[0].read('genOnOff', ['onOff']);
55
+ } catch (error) {
56
+ // Do nothing
57
+ }
58
+ }, 5000);
59
+ globalStore.putValue(device, 'interval', interval);
60
+ }
61
+ },
62
+ }),
42
63
  };
43
64
 
44
65
  module.exports = [
@@ -580,32 +601,21 @@ module.exports = [
580
601
  zigbeeModel: ['GL-G-007Z'],
581
602
  model: 'GL-G-007Z',
582
603
  vendor: 'Gledopto',
583
- description: 'Zigbee 9W Garden Lamp RGB+CCT',
604
+ description: 'Zigbee 9W garden lamp RGB+CCT',
584
605
  extend: gledoptoExtend.light_onoff_brightness_colortemp_color(),
585
606
  },
586
607
  {
587
608
  zigbeeModel: ['GL-W-001Z'],
588
609
  model: 'GL-W-001Z',
589
610
  vendor: 'Gledopto',
590
- description: 'Zigbee On/Off Wall Switch',
591
- extend: extend.switch(),
592
- onEvent: async (type, data, device) => {
593
- // This device doesn't support reporting.
594
- // Therefore we read the on/off state every 5 seconds.
595
- // This is the same way as the Hue bridge does it.
596
- if (type === 'stop') {
597
- clearInterval(globalStore.getValue(device, 'interval'));
598
- globalStore.clearValue(device, 'interval');
599
- } else if (!globalStore.hasValue(device, 'interval')) {
600
- const interval = setInterval(async () => {
601
- try {
602
- await device.endpoints[0].read('genOnOff', ['onOff']);
603
- } catch (error) {
604
- // Do nothing
605
- }
606
- }, 5000);
607
- globalStore.putValue(device, 'interval', interval);
608
- }
609
- },
611
+ description: 'Zigbee on/off wall switch',
612
+ extend: gledoptoExtend.switch(),
613
+ },
614
+ {
615
+ zigbeeModel: ['GL-SD-002'],
616
+ model: 'GL-SD-002',
617
+ vendor: 'Gledopto',
618
+ description: 'Zigbee 3.0 smart home switch',
619
+ extend: gledoptoExtend.switch(),
610
620
  },
611
621
  ];
package/devices/hive.js CHANGED
@@ -183,8 +183,9 @@ module.exports = [
183
183
  exposes.climate().withSetpoint('occupied_heating_setpoint', 5, 32, 0.5).withLocalTemperature().withPiHeatingDemand()
184
184
  .withSystemMode(['heat']).withRunningState(['idle', 'heat'], ea.STATE),
185
185
  exposes.numeric('external_measured_room_sensor', ea.ALL)
186
- .withDescription('Set at maximum 3 hours interval but not more often than every 30 minutes at every 100 ' +
187
- 'value change. Resets every 3hours to standard. e.g. 21C = 2100 (-8000=undefined).'),
186
+ .withDescription('Set at maximum 3 hours interval but not more often than every 30 minutes and 0.1 ' +
187
+ 'degrees difference. Resets every 3hours to standard. e.g. 21C = 2100 (-8000=undefined).')
188
+ .withValueMin(-8000).withValueMax(3500),
188
189
  exposes.numeric('window_open_internal', ea.STATE_GET).withValueMin(0).withValueMax(4)
189
190
  .withDescription('0=Quarantine, 1=Windows are closed, 2=Hold - Windows are maybe about to open, ' +
190
191
  '3=Open window detected, 4=In window open state from external but detected closed locally'),
@@ -203,7 +204,8 @@ module.exports = [
203
204
  .withDescription('Whether or not the thermostat acts as standalone thermostat or shares load with other ' +
204
205
  'thermostats in the room. The gateway must update load_room_mean if enabled.'),
205
206
  exposes.numeric('load_room_mean', ea.ALL)
206
- .withDescription('Mean radiator load for room calculated by gateway for load balancing purposes'),
207
+ .withDescription('Mean radiator load for room calculated by gateway for load balancing purposes (-8000=undefined)')
208
+ .withValueMin(-8000).withValueMax(100),
207
209
  exposes.numeric('load_estimate', ea.STATE_GET)
208
210
  .withDescription('Load estimate on this radiator')],
209
211
  configure: async (device, coordinatorEndpoint, logger) => {
package/devices/icasa.js CHANGED
@@ -81,6 +81,7 @@ module.exports = [
81
81
  model: 'ICZB-RM11S',
82
82
  vendor: 'iCasa',
83
83
  description: 'Zigbee 3.0 remote control',
84
+ meta: {battery: {dontDividePercentage: true}},
84
85
  fromZigbee: [fz.command_recall, fz.command_on, fz.command_off, fz.command_move, fz.command_stop, fz.battery],
85
86
  exposes: [e.battery(), e.action(['recall_*', 'on', 'off', 'brightness_stop', 'brightness_move_up', 'brightness_move_down'])],
86
87
  toZigbee: [],
package/devices/ikea.js CHANGED
@@ -146,10 +146,12 @@ const ikea = {
146
146
  convertSet: async (entity, key, value, meta) => {
147
147
  if (key == 'fan_state' && value.toLowerCase() == 'on') {
148
148
  value = getMetaValue(entity, meta.mapped, 'fanStateOn', 'allEqual', 'on');
149
+ } else {
150
+ value = value.toString().toLowerCase();
149
151
  }
150
152
 
151
153
  let fanMode;
152
- switch (value.toLowerCase()) {
154
+ switch (value) {
153
155
  case 'off':
154
156
  fanMode = 0;
155
157
  break;
@@ -161,7 +163,7 @@ const ikea = {
161
163
  }
162
164
 
163
165
  await entity.write('manuSpecificIkeaAirPurifier', {'fanMode': fanMode}, manufacturerOptions.ikea);
164
- return {state: {fan_mode: value.toLowerCase(), fan_state: value.toLowerCase() === 'off' ? 'OFF' : 'ON'}};
166
+ return {state: {fan_mode: value, fan_state: value === 'off' ? 'OFF' : 'ON'}};
165
167
  },
166
168
  convertGet: async (entity, key, meta) => {
167
169
  await entity.read('manuSpecificIkeaAirPurifier', ['fanMode']);
package/devices/immax.js CHANGED
@@ -140,4 +140,27 @@ module.exports = [
140
140
  description: 'Neo RECUADRO SMART, color temp, dimmable, Zigbee 3.0',
141
141
  extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 370]}),
142
142
  },
143
+ {
144
+ fingerprint: [{modelID: 'TS0202', manufacturerName: '_TZ3210_jijr1sss'}],
145
+ model: '07502L',
146
+ vendor: 'Immax',
147
+ description: '4 in 1 multi sensor',
148
+ fromZigbee: [fz.battery, fz.ignore_basic_report, fz.illuminance, fz.ZB003X, fz.ZB003X_attr, fz.ZB003X_occupancy],
149
+ toZigbee: [tz.ZB003X],
150
+ exposes: [e.occupancy(), e.tamper(), e.battery(), e.illuminance(), e.illuminance_lux().withUnit('lx'), e.temperature(),
151
+ e.humidity(), exposes.numeric('reporting_time', ea.STATE_SET).withDescription('Reporting interval in minutes')
152
+ .withValueMin(0).withValueMax(1440),
153
+ exposes.numeric('temperature_calibration', ea.STATE_SET).withDescription('Temperature calibration')
154
+ .withValueMin(-20).withValueMax(20),
155
+ exposes.numeric('humidity_calibration', ea.STATE_SET).withDescription('Humidity calibration')
156
+ .withValueMin(-50).withValueMax(50),
157
+ exposes.numeric('illuminance_calibration', ea.STATE_SET).withDescription('Illuminance calibration')
158
+ .withValueMin(-10000).withValueMax(10000),
159
+ exposes.binary('pir_enable', ea.STATE_SET, true, false).withDescription('Enable PIR sensor'),
160
+ exposes.binary('led_enable', ea.STATE_SET, true, false).withDescription('Enabled LED'),
161
+ exposes.binary('reporting_enable', ea.STATE_SET, true, false).withDescription('Enabled reporting'),
162
+ exposes.enum('sensitivity', ea.STATE_SET, ['low', 'medium', 'high']).withDescription('PIR sensor sensitivity'),
163
+ // eslint-disable-next-line
164
+ exposes.enum('keep_time', ea.STATE_SET, ['0', '30', '60', '120', '240']).withDescription('PIR keep time in seconds')],
165
+ },
143
166
  ];
package/devices/javis.js CHANGED
@@ -31,6 +31,7 @@ module.exports = [
31
31
  exposes.enum('keep_time', ea.STATE_SET, ['0', '1', '2', '3', '4', '5', '6', '7'])
32
32
  .withDescription('PIR keep time 0:5s|1:30s|2:60s|3:180s|4:300s|5:600s|6:1200s|7:1800s'),
33
33
  exposes.enum('sensitivity', ea.STATE_SET, ['25', '50', '75', '100']),
34
- exposes.numeric('illuminance_calibration', ea.STATE_SET).withDescription('Illuminance calibration')],
34
+ exposes.numeric('illuminance_calibration', ea.STATE_SET).withDescription('Illuminance calibration')
35
+ .withValueMin(-10000).withValueMax(10000)],
35
36
  },
36
37
  ];
@@ -85,7 +85,7 @@ module.exports = [
85
85
  extend: extend.light_onoff_brightness_colortemp(),
86
86
  },
87
87
  {
88
- zigbeeModel: ['ZHA-PIRSensor'],
88
+ fingerprint: [{modelID: 'ZHA-PIRSensor', manufacturerName: 'Leedarson'}],
89
89
  model: '5AA-SS-ZA-H0',
90
90
  vendor: 'Leedarson',
91
91
  description: 'Motion sensor',
package/devices/lidl.js CHANGED
@@ -417,6 +417,7 @@ module.exports = [
417
417
  configure: async (device, coordinatorEndpoint, logger) => {
418
418
  const endpoint = device.getEndpoint(1);
419
419
  await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
420
+ await reporting.batteryPercentageRemaining(endpoint);
420
421
  },
421
422
  },
422
423
  {
@@ -609,7 +610,7 @@ module.exports = [
609
610
  toZigbee: [tz.on_off, tz.lidl_watering_timer],
610
611
  onEvent: tuya.onEventSetTime,
611
612
  configure: async (device, coordinatorEndpoint, logger) => {},
612
- exposes: [e.switch(), exposes.numeric('timer', ea.SET).withValueMin(1)
613
+ exposes: [e.switch(), exposes.numeric('timer', ea.SET).withValueMin(1).withValueMax(10000)
613
614
  .withUnit('min').withDescription('Auto off after specific time.')],
614
615
  },
615
616
  {
@@ -655,8 +656,8 @@ module.exports = [
655
656
  fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_chyvmhay'}],
656
657
  model: '368308_2010',
657
658
  vendor: 'Lidl',
658
- description: 'Silvercrest rdiator valve with thermostat',
659
- fromZigbee: [fz.ignore_tuya_set_time, fzLocal.zs_thermostat, fz.tuya_data_point_dump],
659
+ description: 'Silvercrest radiator valve with thermostat',
660
+ fromZigbee: [fz.ignore_tuya_set_time, fzLocal.zs_thermostat],
660
661
  toZigbee: [tzLocal.zs_thermostat_current_heating_setpoint, tzLocal.zs_thermostat_child_lock,
661
662
  tzLocal.zs_thermostat_comfort_temp, tzLocal.zs_thermostat_eco_temp, tzLocal.zs_thermostat_preset_mode,
662
663
  tzLocal.zs_thermostat_system_mode, tzLocal.zs_thermostat_local_temperature_calibration,
@@ -676,8 +677,10 @@ module.exports = [
676
677
  .withLocalTemperature(ea.STATE).withLocalTemperatureCalibration(-20, 20, 1, ea.STATE_SET)
677
678
  .withSystemMode(['off', 'heat', 'auto'], ea.STATE_SET)
678
679
  .withPreset(['schedule', 'manual', 'holiday', 'boost']),
679
- exposes.numeric('detectwindow_temperature', ea.STATE_SET).withUnit('°C').withDescription('Open window detection temperature'),
680
- exposes.numeric('detectwindow_timeminute', ea.STATE_SET).withUnit('min').withDescription('Open window time in minute'),
680
+ exposes.numeric('detectwindow_temperature', ea.STATE_SET).withUnit('°C').withDescription('Open window detection temperature')
681
+ .withValueMin(-10).withValueMax(35),
682
+ exposes.numeric('detectwindow_timeminute', ea.STATE_SET).withUnit('min').withDescription('Open window time in minute')
683
+ .withValueMin(0).withValueMax(1000),
681
684
  exposes.binary('binary_one', ea.STATE_SET, 'ON', 'OFF').withDescription('Unknown binary one'),
682
685
  exposes.binary('binary_two', ea.STATE_SET, 'ON', 'OFF').withDescription('Unknown binary two'),
683
686
  exposes.binary('away_mode', ea.STATE, 'ON', 'OFF').withDescription('Away mode'),
package/devices/moes.js CHANGED
@@ -225,9 +225,10 @@ module.exports = [
225
225
  'You can set up to 4 stages of temperature every for WEEKDAY ➀➁➂➃➄, SATURDAY ➅ and SUNDAY ➆.'),
226
226
  exposes.binary('boost_heating', ea.STATE_SET, 'ON', 'OFF').withDescription('Boost Heating: press and hold "+" for 3 seconds, ' +
227
227
  'the device will enter the boost heating mode, and the ▷╵◁ will flash. The countdown will be displayed in the APP'),
228
- exposes.numeric('boost_heating_countdown', ea.STATE_SET).withUnit('min').withDescription('Countdown in minutes'),
228
+ exposes.numeric('boost_heating_countdown', ea.STATE_SET).withUnit('min').withDescription('Countdown in minutes')
229
+ .withValueMin(0).withValueMax(15),
229
230
  exposes.numeric('boost_heating_countdown_time_set', ea.STATE_SET).withUnit('second')
230
- .withDescription('Boost Time Setting 100 sec - 900 sec, (default = 300 sec)')],
231
+ .withDescription('Boost Time Setting 100 sec - 900 sec, (default = 300 sec)').withValueMin(100).withValueMax(900)],
231
232
  },
232
233
  {
233
234
  fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_la2c2uo9'}],
@@ -285,34 +286,7 @@ module.exports = [
285
286
  supports: 'open, close, stop, position',
286
287
  fromZigbee: [fz.tuya_cover_options, fz.cover_position_tilt],
287
288
  toZigbee: [tz.cover_state, tz.moes_cover_calibration, tz.cover_position_tilt, tz.tuya_cover_reversal],
288
- exposes: [e.cover_position(), exposes.numeric('calibration_time', ea.ALL), exposes.enum('moving', ea.STATE, ['UP', 'STOP', 'DOWN']),
289
- exposes.binary('motor_reversal', ea.ALL, 'ON', 'OFF')],
290
- },
291
- {
292
- fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_e9ba97vf'},
293
- {modelID: 'TS0601', manufacturerName: '_TZE200_husqqvux'}],
294
- model: 'TV01-ZB',
295
- vendor: 'Moes',
296
- description: 'Thermostat radiator valve',
297
- fromZigbee: [fz.moes_thermostat_tv, fz.ignore_tuya_set_time],
298
- whiteLabel: [{vendor: 'Tesla Smart', model: 'TSL-TRV-TV01ZG'}],
299
- toZigbee: [tz.moes_thermostat_tv],
300
- exposes: [
301
- e.battery(), e.child_lock(), e.window_detection(),
302
- exposes.binary('frost_detection', ea.STATE_SET, true, false).withDescription('Enables/disables frost detection on the device'),
303
- exposes.binary('heating_stop', ea.STATE_SET, true, false).withDescription('Stop heating'),
304
- exposes.numeric('holiday_temperature', ea.STATE_SET).withDescription('Holiday mode temperature'),
305
- exposes.numeric('comfort_temperature', ea.STATE_SET).withDescription('Comfort mode temperature'),
306
- exposes.numeric('eco_temperature', ea.STATE_SET).withDescription('Eco mode temperature'),
307
- exposes.numeric('open_window_temperature', ea.STATE_SET).withDescription('Open window mode temperature'),
308
- exposes.numeric('boost_heating_countdown', ea.STATE).withDescription('Boost heating countdown'),
309
- exposes.numeric('error_status', ea.STATE).withDescription('Error status'),
310
- // exposes.binary('boost_mode', ea.STATE_SET).withDescription('Enables/disables boost mode'),
311
- exposes.climate().withSetpoint('current_heating_setpoint', 5, 29.5, 1, ea.STATE_SET)
312
- .withLocalTemperature(ea.STATE).withLocalTemperatureCalibration(-20, 20, 1, ea.STATE_SET)
313
- .withSystemMode(Object.values(tuya.tvThermostatMode), ea.STATE_SET)
314
- .withPreset(Object.values(tuya.tvThermostatPreset)),
315
- ],
316
- onEvent: tuya.onEventSetLocalTime,
289
+ exposes: [e.cover_position(), exposes.numeric('calibration_time', ea.ALL).withValueMin(0).withValueMax(100),
290
+ exposes.enum('moving', ea.STATE, ['UP', 'STOP', 'DOWN']), exposes.binary('motor_reversal', ea.ALL, 'ON', 'OFF')],
317
291
  },
318
292
  ];
package/devices/neo.js CHANGED
@@ -19,11 +19,11 @@ module.exports = [
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'),
23
- exposes.numeric('temperature_min', ea.STATE_SET).withUnit('°C'),
24
- exposes.numeric('temperature_max', ea.STATE_SET).withUnit('°C'),
25
- exposes.numeric('humidity_min', ea.STATE_SET).withUnit('%'),
26
- exposes.numeric('humidity_max', ea.STATE_SET).withUnit('%'),
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),
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
  ],
@@ -33,4 +33,19 @@ module.exports = [
33
33
  await reporting.batteryPercentageRemaining(endpoint);
34
34
  },
35
35
  },
36
+ {
37
+ fingerprint: [{modelID: 'ZHA-PirSensor', manufacturerName: 'LDS'}],
38
+ model: 'PECMS01',
39
+ vendor: 'Perenio',
40
+ description: 'Motion sensor',
41
+ fromZigbee: [fz.battery, fz.ias_occupancy_alarm_1],
42
+ toZigbee: [],
43
+ meta: {battery: {dontDividePercentage: true}},
44
+ exposes: [e.occupancy(), e.battery_low(), e.tamper(), e.battery()],
45
+ configure: async (device, coordinatorEndpoint, logger) => {
46
+ const endpoint = device.getEndpoint(1);
47
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
48
+ await reporting.batteryPercentageRemaining(endpoint);
49
+ },
50
+ },
36
51
  ];