zigbee-herdsman-converters 14.0.638 → 14.0.640

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.
@@ -3563,6 +3563,18 @@ const converters = {
3563
3563
  return result;
3564
3564
  },
3565
3565
  },
3566
+ danfoss_thermostat_setpoint_scheduled: {
3567
+ cluster: 'hvacThermostat',
3568
+ type: ['attributeReport', 'readResponse'],
3569
+ convert: (model, msg, publish, options, meta) => {
3570
+ const result = {};
3571
+ if (msg.data.hasOwnProperty('occupiedHeatingSetpoint')) {
3572
+ result[postfixWithEndpointName('occupied_heating_setpoint_scheduled', msg, model, meta)] =
3573
+ precisionRound(msg.data['occupiedHeatingSetpoint'], 2) / 100;
3574
+ }
3575
+ return result;
3576
+ },
3577
+ },
3566
3578
  danfoss_icon_battery: {
3567
3579
  cluster: 'genPowerCfg',
3568
3580
  type: ['attributeReport', 'readResponse'],
@@ -2839,6 +2839,8 @@ const converters = {
2839
2839
  key: ['occupied_heating_setpoint'],
2840
2840
  convertSet: async (entity, key, value, meta) => {
2841
2841
  const payload = {
2842
+ // 1: "User Interaction" Changes occupied heating setpoint and triggers an aggressive reaction
2843
+ // of the actuator as soon as control SW runs, to replicate the behavior of turning the dial on the eTRV.
2842
2844
  setpointType: 1,
2843
2845
  setpoint: (Math.round((value * 2).toFixed(1)) / 2).toFixed(1) * 100,
2844
2846
  };
@@ -2848,6 +2850,21 @@ const converters = {
2848
2850
  await entity.read('hvacThermostat', ['occupiedHeatingSetpoint']);
2849
2851
  },
2850
2852
  },
2853
+ danfoss_thermostat_occupied_heating_setpoint_scheduled: {
2854
+ key: ['occupied_heating_setpoint_scheduled'],
2855
+ convertSet: async (entity, key, value, meta) => {
2856
+ const payload = {
2857
+ // 0: "Schedule Change" Just changes occupied heating setpoint. No special behavior,
2858
+ // the PID control setpoint will be update with the new setpoint.
2859
+ setpointType: 0,
2860
+ setpoint: (Math.round((value * 2).toFixed(1)) / 2).toFixed(1) * 100,
2861
+ };
2862
+ await entity.command('hvacThermostat', 'danfossSetpointCommand', payload, manufacturerOptions.danfoss);
2863
+ },
2864
+ convertGet: async (entity, key, meta) => {
2865
+ await entity.read('hvacThermostat', ['occupiedHeatingSetpoint']);
2866
+ },
2867
+ },
2851
2868
  danfoss_mounted_mode_active: {
2852
2869
  key: ['mounted_mode_active'],
2853
2870
  convertGet: async (entity, key, meta) => {
@@ -0,0 +1,11 @@
1
+ const extend = require('../lib/extend');
2
+
3
+ module.exports = [
4
+ {
5
+ zigbeeModel: ['ABL-LIGHT-Z-001'],
6
+ model: 'WF4C_WF6C',
7
+ vendor: 'Acuity Brands Lighting (ABL)',
8
+ description: 'Juno 4" and 6" LED smart wafer downlight',
9
+ extend: extend.light_onoff_brightness_colortemp({colorTempRange: [200, 370]}),
10
+ },
11
+ ];
@@ -30,8 +30,8 @@ module.exports = [
30
30
  .withLocalTemperature(ea.STATE)
31
31
  .withLocalTemperatureCalibration(-9, 9, 1, ea.STATE_SET)
32
32
  .withSystemMode(['heat', 'auto'], ea.STATE_SET)
33
- .withRunningState(['idle', 'heat'], ea.STATE)
34
- .withSensor(['internal', 'external', 'both']),
33
+ .withRunningState(['idle', 'heat'], ea.STATE),
34
+ e.temperature_sensor_select(['internal', 'external', 'both']),
35
35
  exposes.numeric('external_temperature', ea.STATE)
36
36
  .withUnit('°C')
37
37
  .withDescription('Current temperature measured on the external sensor (floor)'),
@@ -17,7 +17,8 @@ module.exports = [
17
17
  description: 'Ally thermostat',
18
18
  whiteLabel: [{vendor: 'Danfoss', model: '014G2463'}],
19
19
  meta: {thermostat: {dontMapPIHeatingDemand: true}},
20
- fromZigbee: [fz.battery, fz.thermostat, fz.thermostat_weekly_schedule, fz.hvac_user_interface, fz.danfoss_thermostat],
20
+ fromZigbee: [fz.battery, fz.thermostat, fz.thermostat_weekly_schedule, fz.hvac_user_interface,
21
+ fz.danfoss_thermostat, fz.danfoss_thermostat_setpoint_scheduled],
21
22
  toZigbee: [tz.danfoss_thermostat_occupied_heating_setpoint, tz.thermostat_local_temperature, tz.danfoss_mounted_mode_active,
22
23
  tz.danfoss_mounted_mode_control, tz.danfoss_thermostat_vertical_orientation, tz.danfoss_algorithm_scale_factor,
23
24
  tz.danfoss_heat_available, tz.danfoss_heat_required, tz.danfoss_day_of_week, tz.danfoss_trigger_time,
@@ -26,7 +27,8 @@ module.exports = [
26
27
  tz.thermostat_keypad_lockout, tz.thermostat_system_mode, tz.danfoss_load_balancing_enable, tz.danfoss_load_room_mean,
27
28
  tz.thermostat_weekly_schedule, tz.thermostat_clear_weekly_schedule, tz.thermostat_programming_operation_mode,
28
29
  tz.danfoss_window_open_feature, tz.danfoss_preheat_status, tz.danfoss_adaptation_status, tz.danfoss_adaptation_settings,
29
- tz.danfoss_adaptation_control, tz.danfoss_regulation_setpoint_offset],
30
+ tz.danfoss_adaptation_control, tz.danfoss_regulation_setpoint_offset,
31
+ tz.danfoss_thermostat_occupied_heating_setpoint_scheduled],
30
32
  exposes: [e.battery(), e.keypad_lockout(), e.programming_operation_mode(),
31
33
  exposes.binary('mounted_mode_active', ea.STATE_GET, true, false)
32
34
  .withDescription('Is the unit in mounting mode. This is set to `false` for mounted (already on ' +
@@ -48,6 +50,11 @@ module.exports = [
48
50
  .withDescription('Values observed are `0` (manual), `1` (schedule) or `2` (externally)'),
49
51
  exposes.climate().withSetpoint('occupied_heating_setpoint', 5, 35, 0.5).withLocalTemperature().withPiHeatingDemand()
50
52
  .withSystemMode(['heat']).withRunningState(['idle', 'heat'], ea.STATE),
53
+ exposes.numeric('occupied_heating_setpoint_scheduled', ea.ALL)
54
+ .withValueMin(5).withValueMax(35).withValueStep(0.5).withUnit('°C')
55
+ .withDescription('Scheduled change of the setpoint. Alternative method for changing the setpoint. In the opposite ' +
56
+ 'to occupied_heating_setpoint it does not trigger an aggressive response from the actuator. ' +
57
+ '(more suitable for scheduled changes)'),
51
58
  exposes.numeric('external_measured_room_sensor', ea.ALL)
52
59
  .withDescription('If `radiator_covered` is `true`: Set at maximum 30 minutes interval but not more often than every ' +
53
60
  '5 minutes and 0.1 degrees difference. Resets every 35 minutes to standard. If `radiator_covered` is `false`: ' +
package/devices/elko.js CHANGED
@@ -61,8 +61,8 @@ module.exports = [
61
61
  exposes.climate().withSetpoint('occupied_heating_setpoint', 5, 50, 1)
62
62
  .withLocalTemperature(ea.STATE)
63
63
  .withLocalTemperatureCalibration(-30, 30, 0.1)
64
- .withSystemMode(['off', 'heat']).withRunningState(['idle', 'heat'])
65
- .withSensor(['air', 'floor', 'supervisor_floor']),
64
+ .withSystemMode(['off', 'heat']).withRunningState(['idle', 'heat']),
65
+ e.temperature_sensor_select(['air', 'floor', 'supervisor_floor']),
66
66
  exposes.numeric('floor_temp', ea.STATE_GET).withUnit('°C')
67
67
  .withDescription('Current temperature measured from the floor sensor'),
68
68
  exposes.numeric('max_floor_temp', ea.ALL).withUnit('°C')
package/devices/hgkg.js CHANGED
@@ -40,8 +40,8 @@ module.exports = [
40
40
  .withSystemMode(['off', 'cool'], ea.STATE_SET)
41
41
  // .withRunningState(['off','on'], ea.STATE)
42
42
  .withPreset(['hold', 'program'])
43
- .withSensor(['IN', 'AL', 'OU'], ea.STATE_SET)
44
43
  .withFanMode(['off', 'low', 'medium', 'high', 'auto'], ea.STATE_SET),
44
+ e.temperature_sensor_select(['IN', 'AL', 'OU']),
45
45
  exposes.composite('programming_mode')
46
46
  .withDescription(
47
47
  'Schedule MODE ⏱ - In this mode, the device executes a preset week programming temperature time and temperature.',
@@ -42,7 +42,7 @@ module.exports = [
42
42
  },
43
43
  {
44
44
  zigbeeModel: ['SV02-410-MP-1.3', 'SV02-412-MP-1.3', 'SV02-610-MP-1.3', 'SV02-612-MP-1.2', 'SV02-612-MP-1.3', 'SV02-410-MP-1.0',
45
- 'SV02-410-MP-1.2'],
45
+ 'SV02-410-MP-1.2', 'SV02-412-MP-1.2'],
46
46
  model: 'SV02',
47
47
  vendor: 'Keen Home',
48
48
  description: 'Smart vent',
@@ -79,4 +79,41 @@ module.exports = [
79
79
  await reporting.fanMode(endpoint);
80
80
  },
81
81
  },
82
+ {
83
+ fingerprint: [{modelID: 'TS011F', manufacturerName: '_TZ3210_pfbzs1an'}],
84
+ model: 'SPUSB02',
85
+ vendor: 'Mercator',
86
+ description: 'Ikuü double power point with USB',
87
+ fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering, fz.ignore_basic_report, fz.tuya_switch_power_outage_memory],
88
+ toZigbee: [tz.on_off],
89
+ exposes: [
90
+ e.switch().withEndpoint('left'),
91
+ e.switch().withEndpoint('right'),
92
+ e.power().withEndpoint('left'),
93
+ e.current().withEndpoint('left'),
94
+ e.voltage().withEndpoint('left').withAccess(ea.STATE),
95
+ e.energy(),
96
+ ],
97
+ endpoint: (device) => {
98
+ return {left: 1, right: 2};
99
+ },
100
+ // The configure method below is needed to make the device reports on/off state changes
101
+ // when the device is controlled manually through the button on it.
102
+ meta: {multiEndpoint: true},
103
+ configure: async (device, coordinatorEndpoint, logger) => {
104
+ const endpoint1 = device.getEndpoint(1);
105
+ const endpoint2 = device.getEndpoint(2);
106
+ await reporting.bind(endpoint1, coordinatorEndpoint, ['genBasic', 'genOnOff', 'haElectricalMeasurement', 'seMetering']);
107
+ await reporting.bind(endpoint2, coordinatorEndpoint, ['genOnOff']);
108
+ await reporting.onOff(endpoint1);
109
+ await reporting.rmsVoltage(endpoint1, {change: 5});
110
+ await reporting.rmsCurrent(endpoint1, {change: 50});
111
+ await reporting.activePower(endpoint1, {change: 1});
112
+ await reporting.onOff(endpoint1);
113
+ await reporting.onOff(endpoint2);
114
+ endpoint1.saveClusterAttributeKeyValue('haElectricalMeasurement', {acCurrentDivisor: 1000, acCurrentMultiplier: 1});
115
+ endpoint1.saveClusterAttributeKeyValue('seMetering', {divisor: 100, multiplier: 1});
116
+ device.save();
117
+ },
118
+ },
82
119
  ];
package/devices/moes.js CHANGED
@@ -186,7 +186,8 @@ module.exports = [
186
186
  exposes.climate().withSetpoint('current_heating_setpoint', 5, 30, 1, ea.STATE_SET)
187
187
  .withLocalTemperature(ea.STATE).withLocalTemperatureCalibration(-30, 30, 0.1, ea.STATE_SET)
188
188
  .withSystemMode(['off', 'heat'], ea.STATE_SET).withRunningState(['idle', 'heat', 'cool'], ea.STATE)
189
- .withPreset(['hold', 'program']).withSensor(['IN', 'AL', 'OU'], ea.STATE_SET)],
189
+ .withPreset(['hold', 'program']),
190
+ e.temperature_sensor_select(['IN', 'AL', 'OU'])],
190
191
  onEvent: tuya.onEventSetLocalTime,
191
192
  },
192
193
  {
@@ -160,9 +160,10 @@ module.exports = [
160
160
  model: '404022/404049C',
161
161
  description: 'Tint dim remote control',
162
162
  vendor: 'Müller Licht',
163
- fromZigbee: [fz.command_on, fz.command_off, fz.command_step, fz.command_move, fz.command_stop, fz.command_move_to_color_temp],
163
+ fromZigbee: [fz.command_on, fz.command_off, fz.command_step, fz.command_move, fz.command_stop, fz.command_move_to_color_temp,
164
+ fz.command_move_to_color, fz.tint_scene],
164
165
  exposes: [e.action(['on', 'off', 'brightness_step_up', 'brightness_step_down', 'brightness_move_up', 'brightness_move_down',
165
- 'brightness_stop', 'color_temperature_move'])],
166
+ 'brightness_stop', 'color_temperature_move', 'color_move', 'scene_1', 'scene_2', 'scene_3', 'scene_4', 'scene_5', 'scene_6'])],
166
167
  toZigbee: [],
167
168
  configure: async (device, coordinatorEndpoint, logger) => {
168
169
  device.powerSource = 'Battery';
package/devices/namron.js CHANGED
@@ -588,7 +588,7 @@ module.exports = [
588
588
  },
589
589
  },
590
590
  {
591
- zigbeeModel: ['5401392', '5401396', '5401393', '5401397', '5401394', '5401398', '5401395', '5401399'],
591
+ zigbeeModel: ['5401392', '5401396', '5401393', '5401397', '5401394', '5401398', '5401395', '5401399', '5401395'],
592
592
  model: '540139X',
593
593
  vendor: 'Namron',
594
594
  description: 'Panel heater 400/600/800/1000 W',
@@ -5,6 +5,7 @@ const reporting = require('../lib/reporting');
5
5
  const utils = require('../lib/utils');
6
6
  const e = exposes.presets;
7
7
  const ea = exposes.access;
8
+ const ota = require('../lib/ota');
8
9
 
9
10
  const switchTypeValues = [
10
11
  'maintained_state',
@@ -254,6 +255,7 @@ module.exports = [
254
255
  await reporting.batteryPercentageRemaining(endpoint);
255
256
  },
256
257
  exposes: [e.water_leak(), e.battery_low(), e.tamper(), e.battery()],
258
+ ota: ota.zigbeeOTA,
257
259
  },
258
260
  {
259
261
  zigbeeModel: ['ZHA-DoorLockSensor'],
@@ -283,6 +285,7 @@ module.exports = [
283
285
  await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
284
286
  await reporting.batteryPercentageRemaining(endpoint);
285
287
  },
288
+ ota: ota.zigbeeOTA,
286
289
  },
287
290
  {
288
291
  zigbeeModel: ['PEHWE20', 'PEHWE2X'],
@@ -405,6 +408,7 @@ module.exports = [
405
408
  exposes.numeric('rssi', ea.STATE).withUnit('dB')
406
409
  .withDescription('RSSI seen by the device').withValueMin(-128).withValueMax(127),
407
410
  ],
411
+ ota: ota.zigbeeOTA,
408
412
  },
409
413
  ];
410
414
 
@@ -136,4 +136,13 @@ module.exports = [
136
136
  device.save();
137
137
  },
138
138
  },
139
+ {
140
+ zigbeeModel: ['3RTHS24BZ'],
141
+ model: '3RTHS24BZ',
142
+ vendor: 'Third Reality',
143
+ description: 'Temperature and humidity sensor',
144
+ fromZigbee: [fz.battery, fz.temperature, fz.humidity],
145
+ toZigbee: [],
146
+ exposes: [e.battery(), e.temperature(), e.humidity(), e.battery_voltage()],
147
+ },
139
148
  ];
@@ -5,7 +5,7 @@ const e = exposes.presets;
5
5
 
6
6
  module.exports = [
7
7
  {
8
- zigbeeModel: ['TPZRCO2HT-Z3'],
8
+ zigbeeModel: ['TPZRCO2HT-Z3', 'TPZRCO2HT-Z3/L'],
9
9
  model: 'TPZRCO2HT-Z3',
10
10
  vendor: 'Titan Products',
11
11
  description: 'Room CO2, humidity & temperature sensor',
package/devices/tuya.js CHANGED
@@ -21,7 +21,7 @@ const TS011Fplugs = ['_TZ3000_5f43h46b', '_TZ3000_cphmq0q7', '_TZ3000_dpo1ysak',
21
21
  '_TZ3000_1h2x4akh', '_TZ3000_9vo5icau', '_TZ3000_cehuw1lw', '_TZ3000_ko6v90pg', '_TZ3000_f1bapcit', '_TZ3000_cjrngdr3',
22
22
  '_TZ3000_zloso4jk', '_TZ3000_r6buo8ba', '_TZ3000_iksasdbv', '_TZ3000_idrffznf', '_TZ3000_okaz9tjs', '_TZ3210_q7oryllx',
23
23
  '_TZ3000_ss98ec5d', '_TZ3000_gznh2xla', '_TZ3000_hdopuwv6', '_TZ3000_gvn91tmx', '_TZ3000_dksbtrzs', '_TZ3000_b28wrpvx',
24
- '_TZ3000_aim0ztek', '_TZ3000_mlswgkc3', '_TZ3000_7dndcnnb', '_TZ3000_waho4jtj'];
24
+ '_TZ3000_aim0ztek', '_TZ3000_mlswgkc3', '_TZ3000_7dndcnnb', '_TZ3000_waho4jtj', '_TZ3000_nmsciidq', '_TZ3000_jtgxgmks'];
25
25
 
26
26
  const tzLocal = {
27
27
  SA12IZL_silence_siren: {
@@ -890,6 +890,7 @@ module.exports = [
890
890
  {modelID: 'TS0505B', manufacturerName: '_TZ3210_it1u8ahz'},
891
891
  {modelID: 'TS0505B', manufacturerName: '_TZ3210_k1pe6ibm'},
892
892
  {modelID: 'TS0505B', manufacturerName: '_TZB210_1ecortg6'},
893
+ {modelID: 'TS0505B', manufacturerName: '_TZ3210_e020aaaj'},
893
894
  {modelID: 'TS0505B', manufacturerName: '_TZ3210_bfwvfyx1'},
894
895
  {modelID: 'TS0505B', manufacturerName: '_TZ3210_leyz4rju'},
895
896
  {modelID: 'TS0505B', manufacturerName: '_TZ3210_jd3z4yig'},
@@ -1173,7 +1174,8 @@ module.exports = [
1173
1174
  fromZigbee: [tuya.fzDataPoints],
1174
1175
  toZigbee: [tuya.tzDataPoints],
1175
1176
  configure: tuya.configureMagicPacket,
1176
- exposes: [tuya.exposes.lightBrightnessWithMinMax, tuya.exposes.powerOnBehavior, tuya.exposes.countdown, tuya.exposes.lightType],
1177
+ exposes: [tuya.exposes.lightBrightnessWithMinMax(), tuya.exposes.powerOnBehavior(),
1178
+ tuya.exposes.countdown(), tuya.exposes.lightType()],
1177
1179
  meta: {
1178
1180
  tuyaDatapoints: [
1179
1181
  [1, 'state', tuya.valueConverter.onOff, {skip: tuya.skip.stateOnAndBrightnessPresent}],
@@ -1294,6 +1296,35 @@ module.exports = [
1294
1296
  return {'l1': 1, 'l2': 1, 'l3': 1, 'l4': 1};
1295
1297
  },
1296
1298
  },
1299
+ {
1300
+ fingerprint: tuya.fingerprint('TS0601', ['_TZE200_jwsjbxjs']),
1301
+ model: 'TS0601_switch_5_gang',
1302
+ vendor: 'TuYa',
1303
+ description: '5 gang switch',
1304
+ fromZigbee: [tuya.fzDataPoints],
1305
+ toZigbee: [tuya.tzDataPoints],
1306
+ configure: tuya.configureMagicPacket,
1307
+ exposes: [
1308
+ tuya.exposes.switch().withEndpoint('l1'),
1309
+ tuya.exposes.switch().withEndpoint('l2'),
1310
+ tuya.exposes.switch().withEndpoint('l3'),
1311
+ tuya.exposes.switch().withEndpoint('l4'),
1312
+ tuya.exposes.switch().withEndpoint('l5'),
1313
+ ],
1314
+ endpoint: (device) => {
1315
+ return {'l1': 1, 'l2': 1, 'l3': 1, 'l4': 1, 'l5': 1};
1316
+ },
1317
+ meta: {
1318
+ multiEndpoint: true,
1319
+ tuyaDatapoints: [
1320
+ [1, 'state_l1', tuya.valueConverter.onOff],
1321
+ [2, 'state_l2', tuya.valueConverter.onOff],
1322
+ [3, 'state_l3', tuya.valueConverter.onOff],
1323
+ [4, 'state_l4', tuya.valueConverter.onOff],
1324
+ [5, 'state_l5', tuya.valueConverter.onOff],
1325
+ ],
1326
+ },
1327
+ },
1297
1328
  {
1298
1329
  fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_nkjintbl'}],
1299
1330
  model: 'TS0601_switch_2_gang',
@@ -1507,24 +1538,6 @@ module.exports = [
1507
1538
  toZigbee: [],
1508
1539
  exposes: [e.battery(), e.temperature(), e.humidity(), e.battery_voltage()],
1509
1540
  },
1510
- {
1511
- zigbeeModel: ['TS0041'],
1512
- fingerprint: [{manufacturerName: '_TZ3000_tk3s5tyg'}],
1513
- model: 'TS0041',
1514
- vendor: 'TuYa',
1515
- description: 'Wireless switch with 1 button',
1516
- whiteLabel: [{vendor: 'Smart9', model: 'S9TSZGB'}, {vendor: 'Lonsonho', model: 'TS0041'}, {vendor: 'Benexmart', model: 'ZM-sui1'}],
1517
- exposes: [e.battery(), e.action(['single', 'double', 'hold'])],
1518
- fromZigbee: [fz.tuya_on_off_action, fz.battery],
1519
- toZigbee: [],
1520
- configure: async (device, coordinatorEndpoint, logger) => {
1521
- const endpoint = device.getEndpoint(1);
1522
- await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
1523
- try {
1524
- await reporting.batteryPercentageRemaining(endpoint);
1525
- } catch (error) {/* Fails for some: https://github.com/Koenkk/zigbee2mqtt/issues/6313 */}
1526
- },
1527
- },
1528
1541
  {
1529
1542
  fingerprint: [{modelID: 'TS011F', manufacturerName: '_TZ3000_3zofvcaa'}],
1530
1543
  model: 'TS011F_2_gang_2_usb_wall',
@@ -1550,6 +1563,24 @@ module.exports = [
1550
1563
  }
1551
1564
  },
1552
1565
  },
1566
+ {
1567
+ zigbeeModel: ['TS0041'],
1568
+ fingerprint: [{manufacturerName: '_TZ3000_tk3s5tyg'}],
1569
+ model: 'TS0041',
1570
+ vendor: 'TuYa',
1571
+ description: 'Wireless switch with 1 button',
1572
+ whiteLabel: [{vendor: 'Smart9', model: 'S9TSZGB'}, {vendor: 'Lonsonho', model: 'TS0041'}, {vendor: 'Benexmart', model: 'ZM-sui1'}],
1573
+ exposes: [e.battery(), e.action(['single', 'double', 'hold'])],
1574
+ fromZigbee: [fz.tuya_on_off_action, fz.battery],
1575
+ toZigbee: [],
1576
+ configure: tuya.configureMagicPacket,
1577
+ /*
1578
+ * reporting.batteryPercentageRemaining removed as it was causing devices to fall of the network
1579
+ * every 1 hour, with light flashing when it happened, extremely short battery life, 2 presses for
1580
+ * action to register: https://github.com/Koenkk/zigbee2mqtt/issues/8072
1581
+ * Initially wrapped in a try catch: https://github.com/Koenkk/zigbee2mqtt/issues/6313
1582
+ */
1583
+ },
1553
1584
  {
1554
1585
  zigbeeModel: ['TS0042'],
1555
1586
  model: 'TS0042',
@@ -1559,11 +1590,13 @@ module.exports = [
1559
1590
  exposes: [e.battery(), e.action(['1_single', '1_double', '1_hold', '2_single', '2_double', '2_hold'])],
1560
1591
  fromZigbee: [fz.tuya_on_off_action, fz.battery],
1561
1592
  toZigbee: [],
1562
- configure: async (device, coordinatorEndpoint, logger) => {
1563
- const endpoint = device.getEndpoint(1);
1564
- await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
1565
- await reporting.batteryPercentageRemaining(endpoint);
1566
- },
1593
+ configure: tuya.configureMagicPacket,
1594
+ /*
1595
+ * reporting.batteryPercentageRemaining removed as it was causing devices to fall of the network
1596
+ * every 1 hour, with light flashing when it happened, extremely short battery life, 2 presses for
1597
+ * action to register: https://github.com/Koenkk/zigbee2mqtt/issues/8072
1598
+ * Initially wrapped in a try catch: https://github.com/Koenkk/zigbee2mqtt/issues/6313
1599
+ */
1567
1600
  },
1568
1601
  {
1569
1602
  zigbeeModel: ['TS0043'],
@@ -1575,13 +1608,13 @@ module.exports = [
1575
1608
  e.action(['1_single', '1_double', '1_hold', '2_single', '2_double', '2_hold', '3_single', '3_double', '3_hold'])],
1576
1609
  fromZigbee: [fz.tuya_on_off_action, fz.battery],
1577
1610
  toZigbee: [],
1578
- configure: async (device, coordinatorEndpoint, logger) => {
1579
- const endpoint = device.getEndpoint(1);
1580
- await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
1581
- try {
1582
- await reporting.batteryPercentageRemaining(endpoint);
1583
- } catch (error) {/* Fails for some*/}
1584
- },
1611
+ configure: tuya.configureMagicPacket,
1612
+ /*
1613
+ * reporting.batteryPercentageRemaining removed as it was causing devices to fall of the network
1614
+ * every 1 hour, with light flashing when it happened, extremely short battery life, 2 presses for
1615
+ * action to register: https://github.com/Koenkk/zigbee2mqtt/issues/8072
1616
+ * Initially wrapped in a try catch: https://github.com/Koenkk/zigbee2mqtt/issues/6313
1617
+ */
1585
1618
  },
1586
1619
  {
1587
1620
  zigbeeModel: ['TS0044'],
@@ -1594,13 +1627,13 @@ module.exports = [
1594
1627
  exposes: [e.battery(), e.action(['1_single', '1_double', '1_hold', '2_single', '2_double', '2_hold',
1595
1628
  '3_single', '3_double', '3_hold', '4_single', '4_double', '4_hold'])],
1596
1629
  toZigbee: [],
1597
- configure: async (device, coordinatorEndpoint, logger) => {
1598
- const endpoint = device.getEndpoint(1);
1599
- await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
1600
- try {
1601
- await reporting.batteryPercentageRemaining(endpoint);
1602
- } catch (error) {/* Fails for some*/}
1603
- },
1630
+ configure: tuya.configureMagicPacket,
1631
+ /*
1632
+ * reporting.batteryPercentageRemaining removed as it was causing devices to fall of the network
1633
+ * every 1 hour, with light flashing when it happened, extremely short battery life, 2 presses for
1634
+ * action to register: https://github.com/Koenkk/zigbee2mqtt/issues/8072
1635
+ * Initially wrapped in a try catch: https://github.com/Koenkk/zigbee2mqtt/issues/6313
1636
+ */
1604
1637
  },
1605
1638
  {
1606
1639
  fingerprint: [{modelID: 'TS004F', manufacturerName: '_TZ3000_xabckq1v'}, {modelID: 'TS004F', manufacturerName: '_TZ3000_czuyt8lz'}],
@@ -1652,7 +1685,7 @@ module.exports = [
1652
1685
  exposes: [e.battery(), e.water_leak()],
1653
1686
  },
1654
1687
  {
1655
- fingerprint: [{modelID: 'TS0001', manufacturerName: '_TZ3000_xkap8wtb'}],
1688
+ fingerprint: [{modelID: 'TS0001', manufacturerName: '_TZ3000_xkap8wtb'}, {modelID: 'TS0001', manufacturerName: '_TZ3000_qnejhcsu'}],
1656
1689
  model: 'TS0001_power',
1657
1690
  description: 'Switch with power monitoring',
1658
1691
  vendor: 'TuYa',
@@ -1670,7 +1703,7 @@ module.exports = [
1670
1703
  endpoint.saveClusterAttributeKeyValue('seMetering', {divisor: 100, multiplier: 1});
1671
1704
  device.save();
1672
1705
  },
1673
- exposes: [e.switch(), e.power(), e.current(), e.voltage().withAccess(ea.STATE)],
1706
+ exposes: [e.switch(), e.power(), e.current(), e.voltage().withAccess(ea.STATE), e.energy()],
1674
1707
  },
1675
1708
  {
1676
1709
  zigbeeModel: ['TS0001'],
@@ -2279,7 +2312,7 @@ module.exports = [
2279
2312
  fromZigbee: [tuya.fzDataPoints],
2280
2313
  toZigbee: [tuya.tzDataPoints],
2281
2314
  configure: tuya.configureMagicPacket,
2282
- exposes: [tuya.exposes.switch, e.ac_frequency(), e.energy(), e.power(), e.power_factor(), e.voltage(), e.current()],
2315
+ exposes: [tuya.exposes.switch(), e.ac_frequency(), e.energy(), e.power(), e.power_factor(), e.voltage(), e.current()],
2283
2316
  meta: {
2284
2317
  tuyaDatapoints: [
2285
2318
  [1, 'energy', tuya.valueConverter.divideBy100],
@@ -2768,7 +2801,8 @@ module.exports = [
2768
2801
  exposes.climate().withSetpoint('current_heating_setpoint', 5, 60, 0.5, ea.STATE_SET)
2769
2802
  .withLocalTemperature(ea.STATE).withLocalTemperatureCalibration(-9.9, 9.9, 0.1, ea.STATE_SET)
2770
2803
  .withSystemMode(['off', 'heat'], ea.STATE_SET).withRunningState(['idle', 'heat'], ea.STATE)
2771
- .withPreset(['manual', 'program']).withSensor(['internal', 'external', 'both'], ea.STATE_SET),
2804
+ .withPreset(['manual', 'program']),
2805
+ e.temperature_sensor_select(['internal', 'external', 'both']),
2772
2806
  exposes.text('schedule', ea.STATE_SET).withDescription('There are 8 periods in the schedule in total. ' +
2773
2807
  '6 for workdays and 2 for holidays. It should be set in the following format for each of the periods: ' +
2774
2808
  '`hours:minutes/temperature`. All periods should be set at once and delimited by the space symbol. ' +
package/devices/xiaomi.js CHANGED
@@ -50,7 +50,7 @@ const fzLocal = {
50
50
  Object.entries(msg.data).forEach(([key, value]) => {
51
51
  switch (parseInt(key)) {
52
52
  case 0x0271:
53
- result['state'] = {1: 'ON', 0: 'OFF'}[value];
53
+ result['system_mode'] = {1: 'heat', 0: 'off'}[value];
54
54
  break;
55
55
  case 0x0272:
56
56
  result['preset'] = {2: 'away', 1: 'auto', 0: 'manual'}[value];
@@ -68,20 +68,27 @@ const fzLocal = {
68
68
  result['away_preset_temperature'] = (value / 100).toFixed(1);
69
69
  break;
70
70
  case 0x027b:
71
- result['calibration'] = {1: true, 0: false}[value];
71
+ result['calibrated'] = {1: true, 0: false}[value];
72
72
  break;
73
73
  case 0x027e:
74
74
  result['sensor'] = {1: 'external', 0: 'internal'}[value];
75
75
  break;
76
+ case 0x040a:
77
+ result['battery'] = value;
78
+ break;
79
+ case 0x027a:
80
+ result['window_open'] = {1: true, 0: false}[value];
81
+ break;
82
+ case 0x0275:
83
+ result['valve_alarm'] = {1: true, 0: false}[value];
84
+ break;
85
+ case 0xfff2:
76
86
  case 0x00ff: // 4e:27:49:bb:24:b6:30:dd:74:de:53:76:89:44:c4:81
77
87
  case 0x00f7: // 03:28:1f:05:21:01:00:0a:21:00:00:0d:23:19:08:00:00:11:23...
78
- case 0x0275: // 0x00000001
79
88
  case 0x0276: // 04:3e:01:e0:00:00:09:60:04:38:00:00:06:a4:05:64:00:00:08:98:81:e0:00:00:08:98
80
- case 0x027a: // 0x00
81
89
  case 0x027c: // 0x00
82
90
  case 0x027d: // 0x00
83
91
  case 0x0280: // 0x00/0x01
84
- case 0x040a: // 0x64
85
92
  meta.logger.debug(`zigbee-herdsman-converters:aqara_trv: Unhandled key ${key} = ${value}`);
86
93
  break;
87
94
  default:
@@ -95,11 +102,19 @@ const fzLocal = {
95
102
 
96
103
  const tzLocal = {
97
104
  aqara_trv: {
98
- key: ['state', 'preset', 'window_detection', 'valve_detection', 'child_lock', 'away_preset_temperature'],
105
+ key: ['system_mode', 'preset', 'window_detection', 'valve_detection', 'child_lock', 'away_preset_temperature',
106
+ 'calibrate', 'sensor', 'sensor_temp', 'identify'],
99
107
  convertSet: async (entity, key, value, meta) => {
108
+ const aqaraHeader = (counter, params, action) => {
109
+ const header = [0xaa, 0x71, params.length + 3, 0x44, counter];
110
+ const integrity = 512 - header.reduce((sum, elem) => sum + elem, 0);
111
+ return [...header, integrity, action, 0x41, params.length];
112
+ };
113
+ const sensor = Buffer.from('00158d00019d1b98', 'hex');
114
+
100
115
  switch (key) {
101
- case 'state':
102
- await entity.write('aqaraOpple', {0x0271: {value: {'OFF': 0, 'ON': 1}[value], type: 0x20}},
116
+ case 'system_mode':
117
+ await entity.write('aqaraOpple', {0x0271: {value: {'off': 0, 'heat': 1}[value], type: 0x20}},
103
118
  {manufacturerCode: 0x115f});
104
119
  break;
105
120
  case 'preset':
@@ -121,10 +136,93 @@ const tzLocal = {
121
136
  case 'away_preset_temperature':
122
137
  await entity.write('aqaraOpple', {0x0279: {value: Math.round(value * 100), type: 0x23}}, {manufacturerCode: 0x115f});
123
138
  break;
139
+ case 'sensor': {
140
+ const device = Buffer.from(entity.deviceIeeeAddress.substring(2), 'hex');
141
+ const timestamp = Buffer.alloc(4);
142
+ timestamp.writeUint32BE(Date.now()/1000);
143
+
144
+ if (value === 'external') {
145
+ const params1 = [
146
+ ...timestamp,
147
+ 0x3d, 0x04,
148
+ ...device,
149
+ ...sensor,
150
+ 0x00, 0x01, 0x00, 0x55,
151
+ 0x13, 0x0a, 0x02, 0x00, 0x00, 0x64, 0x04, 0xce, 0xc2, 0xb6, 0xc8,
152
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3d,
153
+ 0x64,
154
+ 0x65,
155
+ ];
156
+ const params2 = [
157
+ ...timestamp,
158
+ 0x3d, 0x05,
159
+ ...device,
160
+ ...sensor,
161
+ 0x08, 0x00, 0x07, 0xfd,
162
+ 0x16, 0x0a, 0x02, 0x0a, 0xc9, 0xe8, 0xb1, 0xb8, 0xd4, 0xda, 0xcf, 0xdf, 0xc0, 0xeb,
163
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3d,
164
+ 0x04,
165
+ 0x65,
166
+ ];
167
+
168
+ const val1 = [...(aqaraHeader(0x12, params1, 0x02)), ...params1];
169
+ const val2 = [...(aqaraHeader(0x13, params2, 0x02)), ...params2];
170
+
171
+ await entity.write('aqaraOpple', {0xfff2: {value: val1, type: 0x41}}, {manufacturerCode: 0x115f});
172
+ await entity.write('aqaraOpple', {0xfff2: {value: val2, type: 0x41}}, {manufacturerCode: 0x115f});
173
+ } else if (value === 'internal') {
174
+ const params1 = [
175
+ ...timestamp,
176
+ 0x3d, 0x05,
177
+ ...device,
178
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
179
+ ];
180
+ const params2 = [
181
+ ...timestamp,
182
+ 0x3d, 0x04,
183
+ ...device,
184
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
185
+ ];
186
+
187
+ const val1 = [...(aqaraHeader(0x12, params1, 0x04)), ...params1];
188
+ const val2 = [...(aqaraHeader(0x13, params2, 0x04)), ...params2];
189
+
190
+ await entity.write('aqaraOpple', {0xfff2: {value: val1, type: 0x41}}, {manufacturerCode: 0x115f});
191
+ await entity.write('aqaraOpple', {0xfff2: {value: val2, type: 0x41}}, {manufacturerCode: 0x115f});
192
+
193
+ await entity.read('hvacThermostat', ['localTemp']);
194
+ }
195
+ break;
196
+ }
197
+ case 'sensor_temp':
198
+ if (meta.state['sensor'] === 'external') {
199
+ const temperatureBuf = Buffer.alloc(4);
200
+ temperatureBuf.writeFloatBE(Math.round(value * 100));
201
+
202
+ const params = [...sensor, 0x00, 0x01, 0x00, 0x55, ...temperatureBuf];
203
+ const data = [...(aqaraHeader(0x12, params, 0x05)), ...params];
204
+
205
+ await entity.write('aqaraOpple', {0xfff2: {value: data, type: 0x41}}, {manufacturerCode: 0x115f});
206
+ }
207
+ break;
208
+ case 'calibrate':
209
+ await entity.write('aqaraOpple', {0x0270: {value: 1, type: 0x20}}, {manufacturerCode: 0x115F});
210
+ break;
211
+ case 'identify':
212
+ await entity.command('genIdentify', 'identify', {identifytime: 5}, {});
213
+ break;
124
214
  default: // Unknown key
125
215
  meta.logger.warn(`zigbee-herdsman-converters:aqara_trv: Unhandled key ${key}`);
126
216
  }
127
217
  },
218
+ convertGet: async (entity, key, meta) => {
219
+ const dict = {'system_mode': 0x0271, 'preset': 0x0272, 'window_detection': 0x0273, 'valve_detection': 0x0274,
220
+ 'child_lock': 0x0277, 'away_preset_temperature': 0x0279, 'calibrated': 0x027b, 'sensor': 0x027e};
221
+
222
+ if (dict.hasOwnProperty(key)) {
223
+ await entity.read('aqaraOpple', [dict[key]], {manufacturerCode: 0x115F});
224
+ }
225
+ },
128
226
  },
129
227
  VOCKQJK11LM_display_unit: {
130
228
  key: ['display_unit'],
@@ -2282,16 +2380,27 @@ module.exports = [
2282
2380
  description: 'Aqara Smart Radiator Thermostat E1',
2283
2381
  fromZigbee: [fzLocal.aqara_trv, fz.thermostat, fz.battery],
2284
2382
  toZigbee: [tzLocal.aqara_trv, tz.thermostat_occupied_heating_setpoint],
2285
- exposes: [e.switch().setAccess('state', ea.STATE_SET),
2286
- exposes.climate().withSetpoint('occupied_heating_setpoint', 5, 30, 0.5)
2287
- .withLocalTemperature(ea.STATE).withPreset(['manual', 'away', 'auto'], ea.STATE_SET),
2288
- e.child_lock(), e.window_detection(), e.valve_detection(),
2289
- e.away_preset_temperature(), e.battery_voltage(), e.battery(),
2383
+ exposes: [
2384
+ exposes.climate()
2385
+ .withSetpoint('occupied_heating_setpoint', 5, 30, 0.5)
2386
+ .withLocalTemperature(ea.STATE)
2387
+ .withSystemMode(['off', 'heat'], ea.ALL)
2388
+ .withPreset(['manual', 'away', 'auto']).setAccess('preset', ea.ALL),
2389
+ e.temperature_sensor_select(['internal', 'external']).withAccess(ea.ALL),
2390
+ exposes.binary('calibrated', ea.STATE, true, false)
2391
+ .withDescription('Is the valve calibrated'),
2392
+ e.child_lock().setAccess('state', ea.ALL),
2393
+ e.window_detection().setAccess('state', ea.ALL),
2394
+ exposes.binary('window_open', ea.STATE, true, false),
2395
+ e.valve_detection().setAccess('state', ea.ALL),
2396
+ e.away_preset_temperature().withAccess(ea.ALL),
2397
+ e.battery_voltage(),
2398
+ e.battery(),
2290
2399
  ],
2291
2400
  meta: {battery: {voltageToPercentage: '3V_2850_3000'}},
2292
2401
  configure: async (device, coordinatorEndpoint, logger) => {
2293
2402
  const endpoint = device.getEndpoint(1);
2294
- await endpoint.read('genPowerCfg', ['batteryVoltage']);
2403
+ await endpoint.read('aqaraOpple', [0x040a], {manufacturerCode: 0x115f});
2295
2404
  },
2296
2405
  },
2297
2406
  ];
package/lib/exposes.js CHANGED
@@ -424,12 +424,6 @@ class Climate extends Base {
424
424
  return this;
425
425
  }
426
426
 
427
- withSensor(sensors) {
428
- assert(!this.endpoint, 'Cannot add feature after adding endpoint');
429
- this.features.push(new Enum('sensor', access.STATE_SET, sensors).withDescription('Select temperature sensor to use'));
430
- return this;
431
- }
432
-
433
427
  withPreset(modes, description='Mode of this device (similar to system_mode)') {
434
428
  assert(!this.endpoint, 'Cannot add feature after adding endpoint');
435
429
  this.features.push(new Enum('preset', access.STATE_SET, modes).withDescription(description));
@@ -618,6 +612,7 @@ module.exports = {
618
612
  switch_type_2: () => new Enum('switch_type', access.ALL, ['toggle', 'state', 'momentary']).withDescription('Switch type settings'),
619
613
  tamper: () => new Binary('tamper', access.STATE, true, false).withDescription('Indicates whether the device is tampered'),
620
614
  temperature: () => new Numeric('temperature', access.STATE).withUnit('°C').withDescription('Measured temperature value'),
615
+ temperature_sensor_select: (sensor_names) => new Enum('sensor', access.STATE_SET, sensor_names).withDescription('Select temperature sensor to use'),
621
616
  test: () => new Binary('test', access.STATE, true, false).withDescription('Indicates whether the device is being tested'),
622
617
  valve_position: () => new Numeric('position', access.ALL).withValueMin(0).withValueMax(100).withDescription('Position of the valve'),
623
618
  valve_switch: () => new Binary('state', access.ALL, 'OPEN', 'CLOSE').withDescription('Valve state if open or closed'),
package/lib/tuya.js CHANGED
@@ -1137,16 +1137,16 @@ async function sendDataPointStringBuffer(entity, dp, value, cmd, seq=undefined)
1137
1137
  }
1138
1138
 
1139
1139
  const tuyaExposes = {
1140
- powerOnBehavior: exposes.enum('power_on_behavior', ea.STATE_SET, ['off', 'on', 'previous'])
1140
+ powerOnBehavior: () => exposes.enum('power_on_behavior', ea.STATE_SET, ['off', 'on', 'previous'])
1141
1141
  .withDescription('Controls the behavior when the device is powered on'),
1142
- lightType: exposes.enum('light_type', ea.STATE_SET, ['led', 'incandescent', 'halogen'])
1142
+ lightType: () => exposes.enum('light_type', ea.STATE_SET, ['led', 'incandescent', 'halogen'])
1143
1143
  .withDescription('Type of light attached to the device'),
1144
- lightBrightnessWithMinMax: e.light_brightness().withMinBrightness().withMaxBrightness().setAccess(
1144
+ lightBrightnessWithMinMax: () => e.light_brightness().withMinBrightness().withMaxBrightness().setAccess(
1145
1145
  'state', ea.STATE_SET).setAccess('brightness', ea.STATE_SET).setAccess(
1146
1146
  'min_brightness', ea.STATE_SET).setAccess('max_brightness', ea.STATE_SET),
1147
- countdown: exposes.numeric('countdown', ea.STATE_SET).withValueMin(0).withValueMax(43200).withValueStep(1).withUnit('s')
1147
+ countdown: () => exposes.numeric('countdown', ea.STATE_SET).withValueMin(0).withValueMax(43200).withValueStep(1).withUnit('s')
1148
1148
  .withDescription('Countdown to turn device off after a certain time'),
1149
- switch: e.switch().setAccess('state', ea.STATE_SET),
1149
+ switch: () => e.switch().setAccess('state', ea.STATE_SET),
1150
1150
  };
1151
1151
 
1152
1152
  const skip = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "14.0.638",
3
+ "version": "14.0.640",
4
4
  "description": "Collection of device converters to be used with zigbee-herdsman",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -34,11 +34,11 @@
34
34
  },
35
35
  "homepage": "https://github.com/Koenkk/zigbee-herdsman-converters",
36
36
  "dependencies": {
37
- "axios": "^0.27.2",
37
+ "axios": "^1.1.2",
38
38
  "buffer-crc32": "^0.2.13",
39
39
  "https-proxy-agent": "^5.0.1",
40
40
  "tar-stream": "^2.2.0",
41
- "zigbee-herdsman": "^0.14.63"
41
+ "zigbee-herdsman": "^0.14.64"
42
42
  },
43
43
  "devDependencies": {
44
44
  "eslint": "*",