zigbee-herdsman-converters 14.0.680 → 14.0.681

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.
@@ -4260,6 +4260,9 @@ const converters = {
4260
4260
  await entity.write('hvacThermostat', {SinopeOccupancy});
4261
4261
  return {state: {'thermostat_occupancy': value}};
4262
4262
  },
4263
+ convertGet: async (entity, key, meta) => {
4264
+ await entity.read('hvacThermostat', ['SinopeOccupancy']);
4265
+ },
4263
4266
  },
4264
4267
  sinope_thermostat_backlight_autodim_param: {
4265
4268
  key: ['backlight_auto_dim'],
@@ -4269,6 +4272,9 @@ const converters = {
4269
4272
  await entity.write('hvacThermostat', {SinopeBacklight});
4270
4273
  return {state: {'backlight_auto_dim': value}};
4271
4274
  },
4275
+ convertGet: async (entity, key, meta) => {
4276
+ await entity.read('hvacThermostat', ['SinopeBacklight']);
4277
+ },
4272
4278
  },
4273
4279
  sinope_thermostat_enable_outdoor_temperature: {
4274
4280
  key: ['enable_outdoor_temperature'],
package/devices/alecto.js CHANGED
@@ -63,6 +63,7 @@ module.exports = [
63
63
  fingerprint: [
64
64
  {modelID: 'daqwrsj\u0000', manufacturerName: '_TYST11_8daqwrsj'},
65
65
  {modelID: 'TS0601', manufacturerName: '_TZE200_qtbrwrfv'},
66
+ {modelID: 'TS0601', manufacturerName: '_TZE200_8daqwrsj'},
66
67
  ],
67
68
  model: 'SMART-HEAT10',
68
69
  vendor: 'Alecto',
@@ -47,6 +47,19 @@ module.exports = [
47
47
  await reporting.onOff(endpoint);
48
48
  },
49
49
  },
50
+ {
51
+ zigbeeModel: ['DG3HL'],
52
+ model: 'DG3HL-1BW',
53
+ vendor: 'Leviton',
54
+ description: 'Indoor Decora smart Zigbee 3.0 certified plug-in dimmer',
55
+ extend: extend.light_onoff_brightness({disableEffect: true, noConfigure: true}),
56
+ configure: async (device, coordinatorEndpoint, logger) => {
57
+ await extend.light_onoff_brightness().configure(device, coordinatorEndpoint, logger);
58
+ const endpoint = device.getEndpoint(1);
59
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
60
+ await reporting.onOff(endpoint);
61
+ },
62
+ },
50
63
  {
51
64
  zigbeeModel: ['DG15A'],
52
65
  model: 'DG15A-1BW',
@@ -4,47 +4,9 @@ const tz = require('../converters/toZigbee');
4
4
  const reporting = require('../lib/reporting');
5
5
  const extend = require('../lib/extend');
6
6
  const tuya = require('../lib/tuya');
7
- const utils = require('../lib/utils');
8
7
  const e = exposes.presets;
9
8
  const ea = exposes.access;
10
9
 
11
- const fzLocal = {
12
- TS110E_switch_type: {
13
- cluster: 'genLevelCtrl',
14
- type: ['attributeReport', 'readResponse'],
15
- convert: (model, msg, publish, options, meta) => {
16
- const property = 0xfc02;
17
- if (msg.data.hasOwnProperty(property)) {
18
- const value = msg.data[property];
19
- const lookup = {0: 'momentary', 1: 'toggle', 2: 'state'};
20
- if (lookup.hasOwnProperty(value)) {
21
- const propertyName = utils.postfixWithEndpointName('switch_type', msg, model, meta);
22
- return {[propertyName]: lookup[value]};
23
- }
24
- }
25
- },
26
- },
27
- };
28
-
29
- const tzLocal = {
30
- TS110E_switch_type: {
31
- key: ['switch_type'],
32
- convertSet: async (entity, key, value, meta) => {
33
- value = value.toLowerCase();
34
- const lookup = {'momentary': 0, 'toggle': 1, 'state': 2};
35
-
36
- utils.validateValue(value, Object.keys(lookup));
37
- const payload = lookup[value];
38
-
39
- await entity.write('genLevelCtrl', {0xfc02: {value: payload, type: 0x20}});
40
- return {state: {switch_type: value}};
41
- },
42
- convertGet: async (entity, key, meta) => {
43
- await entity.read('genLevelCtrl', [0xfc02]);
44
- },
45
- },
46
- };
47
-
48
10
  module.exports = [
49
11
  {
50
12
  fingerprint: [{modelID: 'TS130F', manufacturerName: '_TZ3000_vd43bbfq'}, {modelID: 'TS130F', manufacturerName: '_TZ3000_fccpjz5z'}],
@@ -260,38 +222,4 @@ module.exports = [
260
222
  exposes.binary('calibration', ea.ALL, 'ON', 'OFF'), exposes.binary('motor_reversal', ea.ALL, 'ON', 'OFF'),
261
223
  exposes.numeric('calibration_time', ea.STATE).withUnit('S').withDescription('Calibration time')],
262
224
  },
263
- {
264
- fingerprint: [{modelID: 'TS110E', manufacturerName: '_TZ3210_zxbtub8r'}],
265
- model: 'TS110E_1gang',
266
- vendor: 'Lonsonho',
267
- description: 'Zigbee smart dimmer module 1 gang with neutral',
268
- extend: extend.light_onoff_brightness(),
269
- },
270
- {
271
- fingerprint: [{modelID: 'TS110E', manufacturerName: '_TZ3210_wdexaypg'}, {modelID: 'TS110E', manufacturerName: '_TZ3210_3mpwqzuu'}],
272
- model: 'TS110E_2gang',
273
- vendor: 'Lonsonho',
274
- description: 'Zigbee smart dimmer module 2 gang with neutral',
275
- fromZigbee: extend.light_onoff_brightness({disablePowerOnBehavior: true, disableMoveStep: true, disableTransition: true})
276
- .fromZigbee.concat([tuya.fz.power_outage_memory, fzLocal.TS110E_switch_type]),
277
- toZigbee: extend.light_onoff_brightness({disablePowerOnBehavior: true, disableMoveStep: true, disableTransition: true})
278
- .toZigbee.concat([tuya.tz.power_on_behavior, tzLocal.TS110E_switch_type]),
279
- meta: {multiEndpoint: true},
280
- exposes: [
281
- e.light_brightness().withEndpoint('l1'),
282
- e.light_brightness().withEndpoint('l2'),
283
- exposes.enum('power_outage_memory', ea.ALL, ['on', 'off', 'restore']).withDescription('Recover state after power outage')
284
- .withEndpoint('l1'),
285
- exposes.presets.switch_type_2().withEndpoint('l1'),
286
- exposes.presets.switch_type_2().withEndpoint('l2'),
287
- ],
288
- configure: async (device, coordinatorEndpoint, logger) => {
289
- await extend.light_onoff_brightness().configure(device, coordinatorEndpoint, logger);
290
- await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff', 'genLevelCtrl']);
291
- await reporting.bind(device.getEndpoint(2), coordinatorEndpoint, ['genOnOff', 'genLevelCtrl']);
292
- },
293
- endpoint: (device) => {
294
- return {l1: 1, l2: 2};
295
- },
296
- },
297
225
  ];
package/devices/sinope.js CHANGED
@@ -14,26 +14,30 @@ module.exports = [
14
14
  vendor: 'Sinopé',
15
15
  description: 'Zigbee line volt thermostat',
16
16
  meta: {thermostat: {dontMapPIHeatingDemand: true}},
17
- fromZigbee: [
18
- fz.legacy.sinope_thermostat_att_report, fz.legacy.hvac_user_interface, fz.electrical_measurement, fz.metering,
17
+ fromZigbee: [fz.legacy.sinope_thermostat_att_report, fz.legacy.hvac_user_interface, fz.electrical_measurement, fz.metering,
19
18
  fz.ignore_temperature_report, fz.legacy.sinope_thermostat_state, fz.sinope_thermostat],
20
- toZigbee: [
21
- tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint,
19
+ toZigbee: [tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint,
22
20
  tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode, tz.thermostat_running_state,
23
- tz.sinope_thermostat_occupancy, tz.sinope_thermostat_backlight_autodim_param, tz.sinope_thermostat_time,
24
- tz.sinope_thermostat_enable_outdoor_temperature, tz.sinope_thermostat_outdoor_temperature, tz.sinope_time_format],
21
+ tz.sinope_thermostat_backlight_autodim_param, tz.sinope_thermostat_time, tz.sinope_thermostat_enable_outdoor_temperature,
22
+ tz.sinope_thermostat_outdoor_temperature, tz.thermostat_pi_heating_demand, tz.sinope_thermostat_occupancy,
23
+ tz.electrical_measurement_power],
25
24
  exposes: [
26
25
  exposes.climate()
27
26
  .withSetpoint('occupied_heating_setpoint', 5, 30, 0.5)
28
27
  .withSetpoint('unoccupied_heating_setpoint', 5, 30, 0.5)
29
28
  .withLocalTemperature()
30
- .withSystemMode(['off', 'heat']).withRunningState(['idle', 'heat'])
31
- .withPiHeatingDemand(),
32
- exposes.enum('thermostat_occupancy', ea.SET, ['unoccupied', 'occupied'])
29
+ .withSystemMode(['off', 'heat'], ea.ALL, 'Mode of the thermostat')
30
+ .withPiHeatingDemand(ea.STATE_GET)
31
+ .withRunningState(['idle', 'heat']),
32
+ exposes.enum('thermostat_occupancy', ea.ALL, ['unoccupied', 'occupied'])
33
33
  .withDescription('Occupancy state of the thermostat'),
34
- exposes.enum('backlight_auto_dim', ea.SET, ['on demand', 'sensing'])
34
+ exposes.enum('backlight_auto_dim', ea.ALL, ['on demand', 'sensing'])
35
35
  .withDescription('Control backlight dimming behavior'),
36
- e.keypad_lockout(), e.energy(), e.power(), e.current(), e.voltage()],
36
+ exposes.enum('keypad_lockout', ea.ALL, ['unlock', 'lock1'])
37
+ .withDescription('Enables or disables the device’s buttons'),
38
+ e.power().withAccess(ea.STATE_GET), e.current(), e.voltage(), e.energy(),
39
+ ],
40
+
37
41
  configure: async (device, coordinatorEndpoint, logger) => {
38
42
  const endpoint = device.getEndpoint(1);
39
43
  const binds = [
@@ -44,36 +48,26 @@ module.exports = [
44
48
  await reporting.thermostatTemperature(endpoint);
45
49
  await reporting.thermostatPIHeatingDemand(endpoint);
46
50
  await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
47
-
48
51
  try {
49
52
  await reporting.thermostatSystemMode(endpoint);
50
53
  } catch (error) {/* Not all support this */}
51
-
52
54
  try {
53
55
  await reporting.thermostatRunningState(endpoint);
54
56
  } catch (error) {/* Not all support this */}
55
57
 
56
58
  await reporting.readMeteringMultiplierDivisor(endpoint);
57
59
  await reporting.currentSummDelivered(endpoint, {min: 10, max: 303, change: [1, 1]});
58
- try {
59
- await reporting.instantaneousDemand(endpoint, {min: 10, max: 304, change: 1});
60
- } catch (error) {/* Do nothing*/}
61
-
62
60
  await reporting.readEletricalMeasurementMultiplierDivisors(endpoint);
63
61
  try {
64
- await reporting.activePower(endpoint, {min: 10, max: 305, change: 1});
65
- } catch (error) {/* Do nothing*/}
66
- try {
67
- await reporting.rmsCurrent(endpoint, {min: 10, max: 306, change: 100}); // divider 1000: 0.1Arms
68
- } catch (error) {/* Do nothing*/}
69
- try {
70
- await reporting.rmsVoltage(endpoint, {min: 10, max: 307, change: 5}); // divider 10: 0.5Vrms
71
- } catch (error) {/* Do nothing*/}
62
+ await endpoint.read('haElectricalMeasurement', ['acPowerMultiplier', 'acPowerDivisor']);
63
+ await reporting.activePower(endpoint, {min: 10, max: 305, change: 1}); // divider 1: 1W
64
+ } catch (error) {
65
+ endpoint.saveClusterAttributeKeyValue('haElectricalMeasurement', {'acPowerMultiplier': 1, 'acPowerDivisor': 1});
66
+ }
67
+ await reporting.rmsCurrent(endpoint, {min: 10, max: 306, change: 100}); // divider 1000: 0.1Arms
68
+ await reporting.rmsVoltage(endpoint, {min: 10, max: 307, change: 5}); // divider 10: 0.5Vrms
72
69
 
73
- // Disable default reporting
74
- await reporting.temperature(endpoint, {min: 1, max: 0xFFFF});
75
- await endpoint.configureReporting('msTemperatureMeasurement', [
76
- {attribute: 'tolerance', minimumReportInterval: 1, maximumReportInterval: 0xFFFF, reportableChange: 1}]);
70
+ await reporting.temperature(endpoint, {min: 1, max: 0xFFFF}); // Disable default reporting
77
71
  },
78
72
  },
79
73
  {
@@ -82,26 +76,30 @@ module.exports = [
82
76
  vendor: 'Sinopé',
83
77
  description: 'Zigbee line volt thermostat',
84
78
  meta: {thermostat: {dontMapPIHeatingDemand: true}},
85
- fromZigbee: [
86
- fz.legacy.sinope_thermostat_att_report, fz.legacy.hvac_user_interface, fz.electrical_measurement, fz.metering,
79
+ fromZigbee: [fz.legacy.sinope_thermostat_att_report, fz.legacy.hvac_user_interface, fz.electrical_measurement, fz.metering,
87
80
  fz.ignore_temperature_report, fz.legacy.sinope_thermostat_state, fz.sinope_thermostat],
88
- toZigbee: [
89
- tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint,
81
+ toZigbee: [tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint,
90
82
  tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode, tz.thermostat_running_state,
91
- tz.sinope_thermostat_occupancy, tz.sinope_thermostat_backlight_autodim_param, tz.sinope_thermostat_time,
92
- tz.sinope_thermostat_enable_outdoor_temperature, tz.sinope_thermostat_outdoor_temperature, tz.sinope_time_format],
83
+ tz.sinope_thermostat_backlight_autodim_param, tz.sinope_thermostat_time, tz.sinope_thermostat_enable_outdoor_temperature,
84
+ tz.sinope_thermostat_outdoor_temperature, tz.thermostat_pi_heating_demand, tz.sinope_thermostat_occupancy,
85
+ tz.electrical_measurement_power],
93
86
  exposes: [
94
87
  exposes.climate()
95
88
  .withSetpoint('occupied_heating_setpoint', 5, 30, 0.5)
96
89
  .withSetpoint('unoccupied_heating_setpoint', 5, 30, 0.5)
97
90
  .withLocalTemperature()
98
- .withSystemMode(['off', 'heat']).withRunningState(['idle', 'heat'])
99
- .withPiHeatingDemand(),
100
- exposes.enum('thermostat_occupancy', ea.SET, ['unoccupied', 'occupied'])
91
+ .withSystemMode(['off', 'heat'], ea.ALL, 'Mode of the thermostat')
92
+ .withPiHeatingDemand(ea.STATE_GET)
93
+ .withRunningState(['idle', 'heat']),
94
+ exposes.enum('thermostat_occupancy', ea.ALL, ['unoccupied', 'occupied'])
101
95
  .withDescription('Occupancy state of the thermostat'),
102
- exposes.enum('backlight_auto_dim', ea.SET, ['on demand', 'sensing'])
96
+ exposes.enum('backlight_auto_dim', ea.ALL, ['on demand', 'sensing'])
103
97
  .withDescription('Control backlight dimming behavior'),
104
- e.keypad_lockout(), e.energy(), e.power(), e.current(), e.voltage()],
98
+ exposes.enum('keypad_lockout', ea.ALL, ['unlock', 'lock1'])
99
+ .withDescription('Enables or disables the device’s buttons'),
100
+ e.power().withAccess(ea.STATE_GET), e.current(), e.voltage(), e.energy(),
101
+ ],
102
+
105
103
  configure: async (device, coordinatorEndpoint, logger) => {
106
104
  const endpoint = device.getEndpoint(1);
107
105
  const binds = [
@@ -112,32 +110,26 @@ module.exports = [
112
110
  await reporting.thermostatTemperature(endpoint);
113
111
  await reporting.thermostatPIHeatingDemand(endpoint);
114
112
  await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
115
-
113
+ try {
114
+ await reporting.thermostatSystemMode(endpoint);
115
+ } catch (error) {/* Not all support this */}
116
116
  try {
117
117
  await reporting.thermostatRunningState(endpoint);
118
118
  } catch (error) {/* Not all support this */}
119
119
 
120
120
  await reporting.readMeteringMultiplierDivisor(endpoint);
121
121
  await reporting.currentSummDelivered(endpoint, {min: 10, max: 303, change: [1, 1]});
122
- try {
123
- await reporting.instantaneousDemand(endpoint, {min: 10, max: 304, change: 1});
124
- } catch (error) {/* Do nothing*/}
125
-
126
122
  await reporting.readEletricalMeasurementMultiplierDivisors(endpoint);
127
123
  try {
128
- await reporting.activePower(endpoint, {min: 10, max: 305, change: 1});
129
- } catch (error) {/* Do nothing*/}
130
- try {
131
- await reporting.rmsCurrent(endpoint, {min: 10, max: 306, change: 100}); // divider 1000: 0.1Arms
132
- } catch (error) {/* Do nothing*/}
133
- try {
134
- await reporting.rmsVoltage(endpoint, {min: 10, max: 307, change: 5}); // divider 10: 0.5Vrms
135
- } catch (error) {/* Do nothing*/}
124
+ await endpoint.read('haElectricalMeasurement', ['acPowerMultiplier', 'acPowerDivisor']);
125
+ await reporting.activePower(endpoint, {min: 10, max: 305, change: 1}); // divider 1: 1W
126
+ } catch (error) {
127
+ endpoint.saveClusterAttributeKeyValue('haElectricalMeasurement', {'acPowerMultiplier': 1, 'acPowerDivisor': 1});
128
+ }
129
+ await reporting.rmsCurrent(endpoint, {min: 10, max: 306, change: 100}); // divider 1000: 0.1Arms
130
+ await reporting.rmsVoltage(endpoint, {min: 10, max: 307, change: 5}); // divider 10: 0.5Vrms
136
131
 
137
- // Disable default reporting
138
- await reporting.temperature(endpoint, {min: 1, max: 0xFFFF});
139
- await endpoint.configureReporting('msTemperatureMeasurement', [
140
- {attribute: 'tolerance', minimumReportInterval: 1, maximumReportInterval: 0xFFFF, reportableChange: 1}]);
132
+ await reporting.temperature(endpoint, {min: 1, max: 0xFFFF}); // Disable default reporting
141
133
  },
142
134
  },
143
135
  {
@@ -163,7 +155,7 @@ module.exports = [
163
155
  .withSystemMode(['off', 'auto', 'heat'])
164
156
  .withRunningState(['idle', 'heat'])
165
157
  .withPiHeatingDemand(),
166
- exposes.enum('backlight_auto_dim', ea.SET, ['on demand', 'sensing'])
158
+ exposes.enum('backlight_auto_dim', ea.ALL, ['on demand', 'sensing'])
167
159
  .withDescription('Control backlight dimming behavior'),
168
160
  e.keypad_lockout(), e.power(), e.current(), e.voltage()],
169
161
  configure: async (device, coordinatorEndpoint, logger) => {
@@ -228,7 +220,7 @@ module.exports = [
228
220
  .withSystemMode(['off', 'auto', 'heat'])
229
221
  .withRunningState(['idle', 'heat'])
230
222
  .withPiHeatingDemand(),
231
- exposes.enum('backlight_auto_dim', ea.SET, ['on demand', 'sensing'])
223
+ exposes.enum('backlight_auto_dim', ea.ALL, ['on demand', 'sensing'])
232
224
  .withDescription('Control backlight dimming behavior')],
233
225
  configure: async (device, coordinatorEndpoint, logger) => {
234
226
  const endpoint = device.getEndpoint(1);
@@ -264,7 +256,7 @@ module.exports = [
264
256
  .withSystemMode(['off', 'auto', 'heat'])
265
257
  .withRunningState(['idle', 'heat'])
266
258
  .withPiHeatingDemand(),
267
- exposes.enum('backlight_auto_dim', ea.SET, ['on demand', 'sensing'])
259
+ exposes.enum('backlight_auto_dim', ea.ALL, ['on demand', 'sensing'])
268
260
  .withDescription('Control backlight dimming behavior')],
269
261
  configure: async (device, coordinatorEndpoint, logger) => {
270
262
  const endpoint = device.getEndpoint(1);
@@ -350,8 +342,8 @@ module.exports = [
350
342
  vendor: 'Sinopé',
351
343
  description: 'Zigbee smart plug',
352
344
  fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering],
353
- toZigbee: [tz.on_off],
354
- exposes: [e.switch(), e.power(), e.current(), e.voltage(), e.ac_frequency(), e.energy()],
345
+ toZigbee: [tz.on_off, tz.frequency],
346
+ exposes: [e.switch(), e.power(), e.current(), e.voltage(), e.energy(), e.ac_frequency().withAccess(ea.STATE_GET)],
355
347
  configure: async (device, coordinatorEndpoint, logger) => {
356
348
  const endpoint = device.getEndpoint(1);
357
349
  const binds = ['genBasic', 'genIdentify', 'genOnOff', 'haElectricalMeasurement', 'seMetering'];
package/devices/sonoff.js CHANGED
@@ -199,7 +199,7 @@ module.exports = [
199
199
  await reporting.batteryVoltage(endpoint);
200
200
  await reporting.batteryPercentageRemaining(endpoint);
201
201
  },
202
- exposes: [e.occupancy(), e.battery_low(), e.tamper(), e.battery(), e.battery_voltage()],
202
+ exposes: [e.occupancy(), e.battery_low(), e.battery(), e.battery_voltage()],
203
203
  },
204
204
  {
205
205
  zigbeeModel: ['S26R2ZB'],
package/devices/tuya.js CHANGED
@@ -17,23 +17,24 @@ const globalStore = require('../lib/store');
17
17
 
18
18
  const tzLocal = {
19
19
  TS110E_options: {
20
- key: ['min_brightness', 'max_brightness', 'light_type'],
20
+ key: ['min_brightness', 'max_brightness', 'light_type', 'switch_type'],
21
21
  convertSet: async (entity, key, value, meta) => {
22
22
  let payload = null;
23
23
  if (key === 'min_brightness' || key == 'max_brightness') {
24
24
  const id = key === 'min_brightness' ? 64515 : 64516;
25
25
  payload = {[id]: {value: utils.mapNumberRange(value, 1, 255, 0, 1000), type: 0x21}};
26
- } else if (key === 'light_type') {
27
- const lookup = {led: 0, incandescent: 1, halogen: 2};
26
+ } else if (key === 'light_type' || key === 'switch_type') {
27
+ const lookup = key === 'light_type' ? {led: 0, incandescent: 1, halogen: 2} : {momentary: 0, toggle: 1, state: 2};
28
28
  payload = {64514: {value: lookup[value], type: 0x20}};
29
29
  }
30
30
  await entity.write('genLevelCtrl', payload, utils.getOptions(meta.mapped, entity));
31
+ return {state: {[key]: value}};
31
32
  },
32
33
  convertGet: async (entity, key, meta) => {
33
34
  let id = null;
34
35
  if (key === 'min_brightness') id = 64515;
35
36
  if (key === 'max_brightness') id = 64516;
36
- if (key === 'light_type') id = 64514;
37
+ if (key === 'light_type' || key === 'switch_type') id = 64514;
37
38
  await entity.read('genLevelCtrl', [id]);
38
39
  },
39
40
  },
@@ -337,12 +338,33 @@ const fzLocal = {
337
338
  if (msg.data.hasOwnProperty('64516')) {
338
339
  result['max_brightness'] = utils.mapNumberRange(msg.data['64516'], 0, 1000, 1, 255);
339
340
  }
341
+ if (msg.data.hasOwnProperty('61440')) {
342
+ result['brightness'] = utils.mapNumberRange(msg.data['61440'], 0, 1000, 0, 254);
343
+ }
344
+ return result;
345
+ },
346
+ },
347
+ TS110E_light_type: {
348
+ cluster: 'genLevelCtrl',
349
+ type: ['attributeReport', 'readResponse'],
350
+ convert: (model, msg, publish, options, meta) => {
351
+ const result = {};
340
352
  if (msg.data.hasOwnProperty('64514')) {
341
353
  const lookup = {0: 'led', 1: 'incandescent', 2: 'halogen'};
342
354
  result['light_type'] = lookup[msg.data['64514']];
343
355
  }
344
- if (msg.data.hasOwnProperty('61440')) {
345
- result['brightness'] = utils.mapNumberRange(msg.data['61440'], 0, 1000, 0, 254);
356
+ return result;
357
+ },
358
+ },
359
+ TS110E_switch_type: {
360
+ cluster: 'genLevelCtrl',
361
+ type: ['attributeReport', 'readResponse'],
362
+ convert: (model, msg, publish, options, meta) => {
363
+ const result = {};
364
+ if (msg.data.hasOwnProperty('64514')) {
365
+ const lookup = {0: 'momentary', 1: 'toggle', 2: 'state'};
366
+ const propertyName = utils.postfixWithEndpointName('switch_type', msg, model, meta);
367
+ result[propertyName] = lookup[msg.data['64514']];
346
368
  }
347
369
  return result;
348
370
  },
@@ -1178,7 +1200,7 @@ module.exports = [
1178
1200
  fromZigbee: [tuya.fz.datapoints],
1179
1201
  toZigbee: [tuya.tz.datapoints],
1180
1202
  configure: tuya.configureMagicPacket,
1181
- exposes: [tuya.exposes.lightBrightnessWithMinMax(), tuya.exposes.powerOnBehavior(),
1203
+ exposes: [tuya.exposes.lightBrightnessWithMinMax(), e.power_on_behavior(),
1182
1204
  tuya.exposes.countdown(), tuya.exposes.lightType()],
1183
1205
  meta: {
1184
1206
  tuyaDatapoints: [
@@ -1734,7 +1756,7 @@ module.exports = [
1734
1756
  endpoint.saveClusterAttributeKeyValue('seMetering', {divisor: 100, multiplier: 1});
1735
1757
  device.save();
1736
1758
  },
1737
- exposes: [e.switch(), e.power(), e.current(), e.voltage().withAccess(ea.STATE), e.energy(), e.switch_type_2(),
1759
+ exposes: [e.switch(), e.power(), e.current(), e.voltage().withAccess(ea.STATE), e.energy(), tuya.exposes.switchType(),
1738
1760
  exposes.enum('power_outage_memory', ea.ALL, ['on', 'off', 'restore']).withDescription('Recover state after power outage')],
1739
1761
  },
1740
1762
  {
@@ -1756,7 +1778,7 @@ module.exports = [
1756
1778
  device.save();
1757
1779
  },
1758
1780
  whiteLabel: [{vendor: 'Aubess', model: 'WDH02'}],
1759
- exposes: [e.switch(), e.power(), e.voltage().withAccess(ea.STATE), e.energy(), e.power_on_behavior(), e.switch_type_2()],
1781
+ exposes: [e.switch(), e.power(), e.voltage().withAccess(ea.STATE), e.energy(), e.power_on_behavior(), tuya.exposes.switchType()],
1760
1782
  },
1761
1783
  {
1762
1784
  zigbeeModel: ['TS0001'],
@@ -2324,7 +2346,7 @@ module.exports = [
2324
2346
  },
2325
2347
  {
2326
2348
  fingerprint: [160, 69, 68, 65, 64].map((applicationVersion) => {
2327
- return {modelID: 'TS011F', applicationVersion};
2349
+ return {modelID: 'TS011F', applicationVersion, priority: -1};
2328
2350
  }),
2329
2351
  model: 'TS011F_plug_3',
2330
2352
  description: 'Smart plug (with power monitoring by polling)',
@@ -3483,23 +3505,56 @@ module.exports = [
3483
3505
  ],
3484
3506
  },
3485
3507
  },
3508
+ {
3509
+ fingerprint: [{modelID: 'TS110E', manufacturerName: '_TZ3210_zxbtub8r'}],
3510
+ model: 'TS110E_1gang_1',
3511
+ vendor: 'TuYa',
3512
+ description: '1 channel dimmer',
3513
+ extend: extend.light_onoff_brightness(),
3514
+ },
3486
3515
  {
3487
3516
  fingerprint: tuya.fingerprint('TS110E', ['_TZ3210_ngqk6jia']),
3488
- model: 'TS110E',
3517
+ model: 'TS110E_1gang_2',
3489
3518
  vendor: 'TuYa',
3490
3519
  description: '1 channel dimmer',
3491
3520
  whiteLabel: [{vendor: 'RTX', model: 'QS-Zigbee-D02-TRIAC-LN'}],
3492
- fromZigbee: [fzLocal.TS110E, tuya.fz.power_on_behavior, fz.on_off],
3521
+ fromZigbee: [fzLocal.TS110E, fzLocal.TS110E_light_type, tuya.fz.power_on_behavior, fz.on_off],
3493
3522
  toZigbee: [tzLocal.TS110E_onoff_brightness, tzLocal.TS110E_options, tuya.tz.power_on_behavior, tz.light_brightness_move],
3494
3523
  exposes: [
3495
3524
  e.light_brightness().withMinBrightness().withMaxBrightness(),
3496
- tuya.exposes.lightType().withAccess(ea.ALL), tuya.exposes.powerOnBehavior().withAccess(ea.ALL)],
3525
+ tuya.exposes.lightType().withAccess(ea.ALL), e.power_on_behavior().withAccess(ea.ALL)],
3497
3526
  configure: async (device, coordinatorEndpoint, logger) => {
3498
3527
  const endpoint = device.getEndpoint(1);
3499
3528
  await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'genLevelCtrl']);
3500
3529
  await reporting.onOff(endpoint);
3501
3530
  },
3502
3531
  },
3532
+ {
3533
+ fingerprint: [{modelID: 'TS110E', manufacturerName: '_TZ3210_wdexaypg'}, {modelID: 'TS110E', manufacturerName: '_TZ3210_3mpwqzuu'}],
3534
+ model: 'TS110E_2gang',
3535
+ vendor: 'TuYa',
3536
+ description: '2 channel dimmer',
3537
+ fromZigbee: extend.light_onoff_brightness({disablePowerOnBehavior: true, disableMoveStep: true, disableTransition: true})
3538
+ .fromZigbee.concat([tuya.fz.power_on_behavior, fzLocal.TS110E_switch_type, fzLocal.TS110E]),
3539
+ toZigbee: extend.light_onoff_brightness({disablePowerOnBehavior: true, disableMoveStep: true, disableTransition: true})
3540
+ .toZigbee.concat([tuya.tz.power_on_behavior, tzLocal.TS110E_options]),
3541
+ meta: {multiEndpoint: true},
3542
+ exposes: [
3543
+ e.light_brightness().withMinBrightness().withMaxBrightness().withEndpoint('l1'),
3544
+ e.light_brightness().withMinBrightness().withMaxBrightness().withEndpoint('l2'),
3545
+ e.power_on_behavior(),
3546
+ tuya.exposes.switchType().withEndpoint('l1'),
3547
+ tuya.exposes.switchType().withEndpoint('l2'),
3548
+ ],
3549
+ configure: async (device, coordinatorEndpoint, logger) => {
3550
+ await extend.light_onoff_brightness().configure(device, coordinatorEndpoint, logger);
3551
+ await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff', 'genLevelCtrl']);
3552
+ await reporting.bind(device.getEndpoint(2), coordinatorEndpoint, ['genOnOff', 'genLevelCtrl']);
3553
+ },
3554
+ endpoint: (device) => {
3555
+ return {l1: 1, l2: 2};
3556
+ },
3557
+ },
3503
3558
  {
3504
3559
  fingerprint: tuya.fingerprint('TS0601', ['_TZE200_nslr42tt']),
3505
3560
  model: 'TS0601_3_phase_clamp_meter',
package/index.js CHANGED
@@ -142,16 +142,23 @@ function findByDevice(device) {
142
142
  return candidates[0];
143
143
  } else {
144
144
  // First try to match based on fingerprint, return the first matching one.
145
+ const fingerprintMatch = {priority: null, definition: null};
145
146
  for (const candidate of candidates) {
146
147
  if (candidate.hasOwnProperty('fingerprint')) {
147
148
  for (const fingerprint of candidate.fingerprint) {
148
- if (fingerprintMatch(fingerprint, device)) {
149
- return candidate;
149
+ const priority = fingerprint.hasOwnProperty('priority') ? fingerprint.priority : 0;
150
+ if (isFingerprintMatch(fingerprint, device) && (!fingerprintMatch.definition || priority > fingerprintMatch.priority)) {
151
+ fingerprintMatch.definition = candidate;
152
+ fingerprintMatch.priority = priority;
150
153
  }
151
154
  }
152
155
  }
153
156
  }
154
157
 
158
+ if (fingerprintMatch.definition) {
159
+ return fingerprintMatch.definition;
160
+ }
161
+
155
162
  // Match based on fingerprint failed, return first matching definition based on zigbeeModel
156
163
  for (const candidate of candidates) {
157
164
  if (candidate.hasOwnProperty('zigbeeModel') && candidate.zigbeeModel.includes(device.modelID)) {
@@ -163,7 +170,7 @@ function findByDevice(device) {
163
170
  return null;
164
171
  }
165
172
 
166
- function fingerprintMatch(fingerprint, device) {
173
+ function isFingerprintMatch(fingerprint, device) {
167
174
  let match =
168
175
  (!fingerprint.applicationVersion || device.applicationVersion === fingerprint.applicationVersion) &&
169
176
  (!fingerprint.manufacturerID || device.manufacturerID === fingerprint.manufacturerID) &&
package/lib/exposes.js CHANGED
@@ -612,7 +612,6 @@ module.exports = {
612
612
  sound_volume: () => new Enum('sound_volume', access.ALL, constants.lockSoundVolume).withDescription('Sound volume of the lock'),
613
613
  switch: () => new Switch().withState('state', true, 'On/off state of the switch'),
614
614
  switch_type: () => new Enum('switch_type', access.ALL, ['toggle', 'momentary']).withDescription('Wall switch type'),
615
- switch_type_2: () => new Enum('switch_type', access.ALL, ['toggle', 'state', 'momentary']).withDescription('Switch type settings'),
616
615
  tamper: () => new Binary('tamper', access.STATE, true, false).withDescription('Indicates whether the device is tampered'),
617
616
  temperature: () => new Numeric('temperature', access.STATE).withUnit('°C').withDescription('Measured temperature value'),
618
617
  temperature_sensor_select: (sensor_names) => new Enum('sensor', access.STATE_SET, sensor_names).withDescription('Select temperature sensor to use'),
package/lib/tuya.js CHANGED
@@ -1117,8 +1117,6 @@ async function sendDataPointStringBuffer(entity, dp, value, cmd, seq=undefined)
1117
1117
  }
1118
1118
 
1119
1119
  const tuyaExposes = {
1120
- powerOnBehavior: () => exposes.enum('power_on_behavior', ea.STATE_SET, ['off', 'on', 'previous'])
1121
- .withDescription('Controls the behavior when the device is powered on'),
1122
1120
  lightType: () => exposes.enum('light_type', ea.STATE_SET, ['led', 'incandescent', 'halogen'])
1123
1121
  .withDescription('Type of light attached to the device'),
1124
1122
  lightBrightnessWithMinMax: () => e.light_brightness().withMinBrightness().withMaxBrightness()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "14.0.680",
3
+ "version": "14.0.681",
4
4
  "description": "Collection of device converters to be used with zigbee-herdsman",
5
5
  "main": "index.js",
6
6
  "files": [