zigbee-herdsman-converters 15.0.4 → 15.0.5

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.
@@ -760,6 +760,31 @@ const converters = {
760
760
  }
761
761
  },
762
762
  },
763
+ light_color_and_colortemp_via_color: {
764
+ key: ['color', 'color_temp', 'color_temp_percent'],
765
+ options: [exposes.options.color_sync(), exposes.options.transition()],
766
+ convertSet: async (entity, key, value, meta) => {
767
+ if (key == 'color') {
768
+ const result = await converters.light_color.convertSet(entity, key, value, meta);
769
+ return result;
770
+ } else if (key == 'color_temp' || key == 'color_temp_percent') {
771
+ const xy = libColor.ColorXY.fromMireds(value);
772
+ const payload = {
773
+ transtime: utils.getTransition(entity, key, meta).time,
774
+ colorx: utils.mapNumberRange(xy.x, 0, 1, 0, 65535),
775
+ colory: utils.mapNumberRange(xy.y, 0, 1, 0, 65535),
776
+ };
777
+ await entity.command('lightingColorCtrl', 'moveToColor', payload, utils.getOptions(meta.mapped, entity));
778
+ return {
779
+ state: libColor.syncColorState({'color_mode': constants.colorMode[2], 'color_temp': value}, meta.state,
780
+ entity, meta.options, meta.logger), readAfterWriteTime: payload.transtime * 100,
781
+ };
782
+ }
783
+ },
784
+ convertGet: async (entity, key, meta) => {
785
+ await entity.read('lightingColorCtrl', light.readColorAttributes(entity, meta));
786
+ },
787
+ },
763
788
  light_hue_saturation_step: {
764
789
  key: ['hue_step', 'saturation_step'],
765
790
  options: [exposes.options.transition()],
@@ -5516,7 +5541,9 @@ const converters = {
5516
5541
  extensionfieldsets.push({'clstId': 8, 'len': 1, 'extField': [val]});
5517
5542
  state['brightness'] = val;
5518
5543
  } else if (attribute === 'position') {
5519
- extensionfieldsets.push({'clstId': 258, 'len': 1, 'extField': [100 - val]});
5544
+ const invert = utils.getMetaValue(entity, meta.mapped, 'coverInverted', 'allEqual', false) ?
5545
+ !meta.options.invert_cover : meta.options.invert_cover;
5546
+ extensionfieldsets.push({'clstId': 258, 'len': 1, 'extField': [invert ? 100 - val : val]});
5520
5547
  state['position'] = val;
5521
5548
  } else if (attribute === 'color_temp') {
5522
5549
  /*
package/devices/ikea.js CHANGED
@@ -6,7 +6,6 @@ const constants = require('../lib/constants');
6
6
  const reporting = require('../lib/reporting');
7
7
  const {repInterval} = require('../lib/constants');
8
8
  const utils = require('../lib/utils');
9
- const libColor = require('../lib/color');
10
9
  const extend = require('../lib/extend');
11
10
  const globalStore = require('../lib/store');
12
11
  const e = exposes.presets;
@@ -73,30 +72,6 @@ const configureRemote = async (device, coordinatorEndpoint, logger) => {
73
72
  await reporting.batteryPercentageRemaining(endpoint);
74
73
  };
75
74
 
76
- const tzLocal = {
77
- LED1624G9_color_colortemp: {
78
- ...tz.light_color_colortemp,
79
- convertSet: async (entity, key, value, meta) => {
80
- if (key == 'color') {
81
- const result = await tz.light_color.convertSet(entity, key, value, meta);
82
- return result;
83
- } else if (key == 'color_temp' || key == 'color_temp_percent') {
84
- const xy = libColor.ColorXY.fromMireds(value);
85
- const payload = {
86
- transtime: utils.getTransition(entity, key, meta).time,
87
- colorx: utils.mapNumberRange(xy.x, 0, 1, 0, 65535),
88
- colory: utils.mapNumberRange(xy.y, 0, 1, 0, 65535),
89
- };
90
- await entity.command('lightingColorCtrl', 'moveToColor', payload, utils.getOptions(meta.mapped, entity));
91
- return {
92
- state: libColor.syncColorState({'color_mode': constants.colorMode[2], 'color_temp': value}, meta.state,
93
- entity, meta.options, meta.logger), readAfterWriteTime: payload.transtime * 100,
94
- };
95
- }
96
- },
97
- },
98
- };
99
-
100
75
  const fzLocal = {
101
76
  // The STYRBAR sends an on +- 500ms after the arrow release. We don't want to send the ON action in this case.
102
77
  // https://github.com/Koenkk/zigbee2mqtt/issues/13335
@@ -490,7 +465,7 @@ module.exports = [
490
465
  toZigbee: utils.replaceInArray(
491
466
  tradfriExtend.light_onoff_brightness_colortemp_color().toZigbee,
492
467
  [tz.light_color_colortemp],
493
- [tzLocal.LED1624G9_color_colortemp],
468
+ [tz.light_color_and_colortemp_via_color],
494
469
  ),
495
470
  meta: {supportsHueAndSaturation: false},
496
471
  },
package/devices/iris.js CHANGED
@@ -58,7 +58,7 @@ module.exports = [
58
58
  await reporting.temperature(endpoint);
59
59
  await reporting.batteryVoltage(endpoint);
60
60
  },
61
- exposes: [e.occupancy(), e.battery_low(), e.tamper(), e.temperature(), e.battery()],
61
+ exposes: [e.occupancy(), e.battery_low(), e.temperature(), e.battery()],
62
62
  },
63
63
  {
64
64
  zigbeeModel: ['3320-L'],
@@ -74,7 +74,7 @@ module.exports = [
74
74
  await reporting.temperature(endpoint);
75
75
  await reporting.batteryVoltage(endpoint);
76
76
  },
77
- exposes: [e.contact(), e.battery_low(), e.tamper(), e.temperature(), e.battery()],
77
+ exposes: [e.contact(), e.battery_low(), e.temperature(), e.battery()],
78
78
  },
79
79
  {
80
80
  zigbeeModel: ['3450-L', '3450-L2'],
@@ -251,6 +251,22 @@ module.exports = [
251
251
  extend: extend.ledvance.light_onoff_brightness(),
252
252
  ota: ota.ledvance,
253
253
  },
254
+ {
255
+ zigbeeModel: ['FLEX RGBW T'],
256
+ model: '4058075729384',
257
+ vendor: 'LEDVANCE',
258
+ description: 'SMART+ Indoor Flex multicolor RGBW',
259
+ extend: extend.ledvance.light_onoff_brightness_colortemp_color({colorTempRange: [153, 526]}),
260
+ ota: ota.ledvance,
261
+ },
262
+ {
263
+ zigbeeModel: ['OUTDOOR FLEX RGBW T'],
264
+ model: '4058075729360',
265
+ vendor: 'LEDVANCE',
266
+ description: 'SMART+ Outdoor Flex multicolor RGBW',
267
+ extend: extend.ledvance.light_onoff_brightness_colortemp_color({colorTempRange: [153, 526]}),
268
+ ota: ota.ledvance,
269
+ },
254
270
  {
255
271
  zigbeeModel: ['GLOBE60 FIL DIM T'],
256
272
  model: '4058075729247',
package/devices/lidl.js CHANGED
@@ -8,6 +8,7 @@ const ea = exposes.access;
8
8
  const tuya = require('../lib/tuya');
9
9
  const globalStore = require('../lib/store');
10
10
  const ota = require('../lib/ota');
11
+ const utils = require('../lib/utils');
11
12
 
12
13
  const tuyaLocal = {
13
14
  dataPoints: {
@@ -542,6 +543,11 @@ module.exports = [
542
543
  vendor: 'Lidl',
543
544
  description: 'Livarno smart LED ceiling light',
544
545
  ...extend.light_onoff_brightness_colortemp_color({disableColorTempStartup: true, colorTempRange: [153, 500]}),
546
+ toZigbee: utils.replaceInArray(
547
+ extend.light_onoff_brightness_colortemp_color({disableColorTempStartup: true, colorTempRange: [153, 500]}).toZigbee,
548
+ [tz.light_color_colortemp],
549
+ [tz.light_color_and_colortemp_via_color],
550
+ ),
545
551
  meta: {applyRedFix: true, enhancedHue: false},
546
552
  configure: async (device, coordinatorEndpoint, logger) => {
547
553
  device.getEndpoint(1).saveClusterAttributeKeyValue('lightingColorCtrl', {colorCapabilities: 29});
@@ -25,7 +25,7 @@ module.exports = [
25
25
  model: '11830304',
26
26
  vendor: 'Lonsonho',
27
27
  description: 'Curtain switch',
28
- fromZigbee: [fz.cover_position_tilt, tuya.fz.backlight_mode, fz.tuya_cover_options],
28
+ fromZigbee: [fz.cover_position_tilt, tuya.fz.backlight_mode_low_medium_high, fz.tuya_cover_options],
29
29
  toZigbee: [tz.cover_state, tz.cover_position_tilt, tz.tuya_cover_calibration, tz.tuya_cover_reversal,
30
30
  tuya.tz.backlight_indicator_mode],
31
31
  meta: {coverInverted: true},
package/devices/moes.js CHANGED
@@ -399,7 +399,7 @@ module.exports = [
399
399
  model: 'ZS-EUB_1gang',
400
400
  vendor: 'Moes',
401
401
  description: 'Wall light switch (1 gang)',
402
- extend: tuya.extend.switch({backlightMode: true, switchType: true}),
402
+ extend: tuya.extend.switch({backlightModeLowMediumHigh: true, switchType: true}),
403
403
  configure: async (device, coordinatorEndpoint, logger) => {
404
404
  await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff']);
405
405
  device.powerSource = 'Mains (single phase)';
@@ -810,6 +810,13 @@ module.exports = [
810
810
  description: 'Hue Filament Standard A60/E27 bluetooth',
811
811
  extend: hueExtend.light_onoff_brightness(),
812
812
  },
813
+ {
814
+ zigbeeModel: ['LWA021'],
815
+ model: '9290030514',
816
+ vendor: 'Philips',
817
+ description: 'Hue Filament Standard A60/E27 bluetooth',
818
+ extend: hueExtend.light_onoff_brightness(),
819
+ },
813
820
  {
814
821
  zigbeeModel: ['LCB001'],
815
822
  model: '548727',
package/devices/sinope.js CHANGED
@@ -8,6 +8,7 @@ const extend = require('../lib/extend');
8
8
  const e = exposes.presets;
9
9
  const ea = exposes.access;
10
10
 
11
+ const manuSinope = {manufacturerCode: 0x119C};
11
12
  const fzLocal = {
12
13
  sinope_thermostat: {
13
14
  cluster: 'hvacThermostat',
@@ -123,11 +124,11 @@ const tzLocal = {
123
124
  convertSet: async (entity, key, value, meta) => {
124
125
  const sinopeOccupancy = {0: 'unoccupied', 1: 'occupied'};
125
126
  const SinopeOccupancy = utils.getKey(sinopeOccupancy, value, value, Number);
126
- await entity.write('hvacThermostat', {SinopeOccupancy}, {manufacturerCode: 0x119C});
127
+ await entity.write('hvacThermostat', {SinopeOccupancy}, manuSinope);
127
128
  return {state: {'thermostat_occupancy': value}};
128
129
  },
129
130
  convertGet: async (entity, key, meta) => {
130
- await entity.read('hvacThermostat', ['SinopeOccupancy'], {manufacturerCode: 0x119C});
131
+ await entity.read('hvacThermostat', ['SinopeOccupancy'], manuSinope);
131
132
  },
132
133
  },
133
134
  sinope_thermostat_backlight_autodim_param: {
@@ -135,11 +136,11 @@ const tzLocal = {
135
136
  convertSet: async (entity, key, value, meta) => {
136
137
  const sinopeBacklightParam = {0: 'on_demand', 1: 'sensing'};
137
138
  const SinopeBacklight = utils.getKey(sinopeBacklightParam, value, value, Number);
138
- await entity.write('hvacThermostat', {SinopeBacklight}, {manufacturerCode: 0x119C});
139
+ await entity.write('hvacThermostat', {SinopeBacklight}, manuSinope);
139
140
  return {state: {'backlight_auto_dim': value}};
140
141
  },
141
142
  convertGet: async (entity, key, meta) => {
142
- await entity.read('hvacThermostat', ['SinopeBacklight'], {manufacturerCode: 0x119C});
143
+ await entity.read('hvacThermostat', ['SinopeBacklight'], manuSinope);
143
144
  },
144
145
  },
145
146
  sinope_thermostat_main_cycle_output: {
@@ -147,11 +148,11 @@ const tzLocal = {
147
148
  key: ['main_cycle_output'],
148
149
  convertSet: async (entity, key, value, meta) => {
149
150
  const lookup = {'15_sec': 15, '5_min': 300, '10_min': 600, '15_min': 900, '20_min': 1200, '30_min': 1800};
150
- await entity.write('hvacThermostat', {SinopeMainCycleOutput: lookup[value]}, {manufacturerCode: 0x119C});
151
+ await entity.write('hvacThermostat', {SinopeMainCycleOutput: lookup[value]}, manuSinope);
151
152
  return {state: {'main_cycle_output': value}};
152
153
  },
153
154
  convertGet: async (entity, key, meta) => {
154
- await entity.read('hvacThermostat', ['SinopeMainCycleOutput'], {manufacturerCode: 0x119C});
155
+ await entity.read('hvacThermostat', ['SinopeMainCycleOutput'], manuSinope);
155
156
  },
156
157
  },
157
158
  sinope_thermostat_aux_cycle_output: {
@@ -170,22 +171,22 @@ const tzLocal = {
170
171
  key: ['enable_outdoor_temperature'],
171
172
  convertSet: async (entity, key, value, meta) => {
172
173
  if (value.toLowerCase() == 'on') {
173
- await entity.write('manuSpecificSinope', {outdoorTempToDisplayTimeout: 10800});
174
+ await entity.write('manuSpecificSinope', {outdoorTempToDisplayTimeout: 10800}, manuSinope);
174
175
  } else if (value.toLowerCase() == 'off') {
175
176
  // set timer to 12 sec in order to disable outdoor temperature
176
- await entity.write('manuSpecificSinope', {outdoorTempToDisplayTimeout: 12});
177
+ await entity.write('manuSpecificSinope', {outdoorTempToDisplayTimeout: 12}, manuSinope);
177
178
  }
178
179
  return {readAfterWriteTime: 250, state: {enable_outdoor_temperature: value}};
179
180
  },
180
181
  convertGet: async (entity, key, meta) => {
181
- await entity.read('manuSpecificSinope', ['outdoorTempToDisplayTimeout']);
182
+ await entity.read('manuSpecificSinope', ['outdoorTempToDisplayTimeout'], manuSinope);
182
183
  },
183
184
  },
184
185
  sinope_thermostat_outdoor_temperature: {
185
186
  key: ['thermostat_outdoor_temperature'],
186
187
  convertSet: async (entity, key, value, meta) => {
187
188
  if (value > -100 && value < 100) {
188
- await entity.write('manuSpecificSinope', {outdoorTempToDisplay: value * 100});
189
+ await entity.write('manuSpecificSinope', {outdoorTempToDisplay: value * 100}, manuSinope);
189
190
  }
190
191
  },
191
192
  },
@@ -197,9 +198,9 @@ const tzLocal = {
197
198
  const thermostatTimeSec = thermostatDate.getTime() / 1000;
198
199
  const thermostatTimezoneOffsetSec = thermostatDate.getTimezoneOffset() * 60;
199
200
  const currentTimeToDisplay = Math.round(thermostatTimeSec - thermostatTimezoneOffsetSec - 946684800);
200
- await entity.write('manuSpecificSinope', {currentTimeToDisplay});
201
+ await entity.write('manuSpecificSinope', {currentTimeToDisplay}, manuSinope);
201
202
  } else if (value !== '') {
202
- await entity.write('manuSpecificSinope', {currentTimeToDisplay: value});
203
+ await entity.write('manuSpecificSinope', {currentTimeToDisplay: value}, manuSinope);
203
204
  }
204
205
  },
205
206
  },
@@ -287,12 +288,12 @@ const tzLocal = {
287
288
  const lookup = {'24h': 0, '12h': 1};
288
289
  value = value.toLowerCase();
289
290
  if (lookup.hasOwnProperty(value)) {
290
- await entity.write('manuSpecificSinope', {timeFormatToDisplay: lookup[value]});
291
+ await entity.write('manuSpecificSinope', {timeFormatToDisplay: lookup[value]}, manuSinope);
291
292
  return {readAfterWriteTime: 250, state: {time_format: value}};
292
293
  }
293
294
  },
294
295
  convertGet: async (entity, key, meta) => {
295
- await entity.read('manuSpecificSinope', ['timeFormatToDisplay']);
296
+ await entity.read('manuSpecificSinope', ['timeFormatToDisplay'], manuSinope);
296
297
  },
297
298
  },
298
299
  sinope_connected_load: {
@@ -428,6 +429,10 @@ module.exports = [
428
429
  await reporting.thermostatTemperature(endpoint);
429
430
  await reporting.thermostatPIHeatingDemand(endpoint);
430
431
  await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
432
+
433
+ await reporting.temperature(endpoint, {min: 1, max: 0xFFFF}); // Disable default reporting
434
+ await endpoint.configureReporting('msTemperatureMeasurement', [{
435
+ attribute: 'tolerance', minimumReportInterval: 1, maximumReportInterval: 0xFFFF, reportableChange: 1}]);
431
436
  try {
432
437
  await reporting.thermostatSystemMode(endpoint);
433
438
  } catch (error) {/* Not all support this */}
@@ -446,8 +451,6 @@ module.exports = [
446
451
  }
447
452
  await reporting.rmsCurrent(endpoint, {min: 10, max: 306, change: 100}); // divider 1000: 0.1Arms
448
453
  await reporting.rmsVoltage(endpoint, {min: 10, max: 307, change: 5}); // divider 10: 0.5Vrms
449
-
450
- await reporting.temperature(endpoint, {min: 1, max: 0xFFFF}); // Disable default reporting
451
454
  },
452
455
  },
453
456
  {
@@ -492,6 +495,10 @@ module.exports = [
492
495
  await reporting.thermostatTemperature(endpoint);
493
496
  await reporting.thermostatPIHeatingDemand(endpoint);
494
497
  await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
498
+
499
+ await reporting.temperature(endpoint, {min: 1, max: 0xFFFF}); // Disable default reporting
500
+ await endpoint.configureReporting('msTemperatureMeasurement', [{
501
+ attribute: 'tolerance', minimumReportInterval: 1, maximumReportInterval: 0xFFFF, reportableChange: 1}]);
495
502
  try {
496
503
  await reporting.thermostatSystemMode(endpoint);
497
504
  } catch (error) {/* Not all support this */}
@@ -510,8 +517,6 @@ module.exports = [
510
517
  }
511
518
  await reporting.rmsCurrent(endpoint, {min: 10, max: 306, change: 100}); // divider 1000: 0.1Arms
512
519
  await reporting.rmsVoltage(endpoint, {min: 10, max: 307, change: 5}); // divider 10: 0.5Vrms
513
-
514
- await reporting.temperature(endpoint, {min: 1, max: 0xFFFF}); // Disable default reporting
515
520
  },
516
521
  },
517
522
  {
@@ -557,22 +562,24 @@ module.exports = [
557
562
  const thermostatTimeSec = thermostatDate.getTime() / 1000;
558
563
  const thermostatTimezoneOffsetSec = thermostatDate.getTimezoneOffset() * 60;
559
564
  const currentTimeToDisplay = Math.round(thermostatTimeSec - thermostatTimezoneOffsetSec - 946684800);
560
- await endpoint.write('manuSpecificSinope', {currentTimeToDisplay}, {manufacturerCode: 0x119C});
561
- await endpoint.write('manuSpecificSinope', {'secondScreenBehavior': 0}, {manufacturerCode: 0x119C}); // Mode auto
565
+ await endpoint.write('manuSpecificSinope', {currentTimeToDisplay}, manuSinope);
566
+ await endpoint.write('manuSpecificSinope', {'secondScreenBehavior': 0}, manuSinope); // Mode auto
562
567
 
563
568
  await reporting.thermostatTemperature(endpoint);
564
569
  await reporting.thermostatPIHeatingDemand(endpoint);
565
570
  await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
566
571
  await reporting.thermostatSystemMode(endpoint);
567
572
 
573
+ await reporting.temperature(endpoint, {min: 1, max: 0xFFFF}); // Disable default reporting
574
+ await endpoint.configureReporting('msTemperatureMeasurement', [{
575
+ attribute: 'tolerance', minimumReportInterval: 1, maximumReportInterval: 0xFFFF, reportableChange: 1}]);
576
+
568
577
  await reporting.readMeteringMultiplierDivisor(endpoint);
569
578
  await reporting.currentSummDelivered(endpoint, {min: 10, max: 303, change: [1, 1]});
570
579
  await reporting.readEletricalMeasurementMultiplierDivisors(endpoint);
571
580
  await reporting.activePower(endpoint, {min: 10, max: 305, change: 1}); // divider 1: 1W
572
581
  await reporting.rmsCurrent(endpoint, {min: 10, max: 306, change: 100}); // divider 1000: 0.1Arms
573
582
  await reporting.rmsVoltage(endpoint, {min: 10, max: 307, change: 5}); // divider 10: 0.5Vrms
574
-
575
- await reporting.temperature(endpoint, {min: 1, max: 0xFFFF}); // Disable default reporting
576
583
  try {
577
584
  await reporting.thermostatUnoccupiedHeatingSetpoint(endpoint);
578
585
  } catch (error) {/* Do nothing */} // Not enought space but shall pass
package/devices/tuya.js CHANGED
@@ -931,7 +931,7 @@ module.exports = [
931
931
  model: 'TS011F_2_gang_wall',
932
932
  vendor: 'TuYa',
933
933
  description: '2 gang wall outlet',
934
- extend: tuya.extend.switch({backlightMode: true, childLock: true, endpoints: ['l1', 'l2']}),
934
+ extend: tuya.extend.switch({backlightModeLowMediumHigh: true, childLock: true, endpoints: ['l1', 'l2']}),
935
935
  whiteLabel: [{vendor: 'ClickSmart+', model: 'CMA30036'}],
936
936
  endpoint: (device) => {
937
937
  return {'l1': 1, 'l2': 2};
@@ -1389,7 +1389,7 @@ module.exports = [
1389
1389
  model: 'TS130F',
1390
1390
  vendor: 'TuYa',
1391
1391
  description: 'Curtain/blind switch',
1392
- fromZigbee: [fz.cover_position_tilt, tuya.fz.backlight_mode, fz.tuya_cover_options],
1392
+ fromZigbee: [fz.cover_position_tilt, tuya.fz.backlight_mode_low_medium_high, fz.tuya_cover_options],
1393
1393
  toZigbee: [tz.cover_state, tz.cover_position_tilt, tz.tuya_cover_calibration, tz.tuya_cover_reversal,
1394
1394
  tuya.tz.backlight_indicator_mode],
1395
1395
  meta: {coverInverted: true},
@@ -1676,7 +1676,7 @@ module.exports = [
1676
1676
  model: 'TS011F_2_gang_2_usb_wall',
1677
1677
  vendor: 'TuYa',
1678
1678
  description: '2 gang 2 usb wall outlet',
1679
- extend: tuya.extend.switch({backlightMode: true, endpoints: ['l1', 'l2', 'l3', 'l4']}),
1679
+ extend: tuya.extend.switch({backlightModeLowMediumHigh: true, endpoints: ['l1', 'l2', 'l3', 'l4']}),
1680
1680
  endpoint: () => {
1681
1681
  return {'l1': 1, 'l2': 2, 'l3': 3, 'l4': 4};
1682
1682
  },
@@ -2766,8 +2766,7 @@ module.exports = [
2766
2766
  description: 'Smart light switch - 2 gang',
2767
2767
  whiteLabel: [{vendor: 'Vrey', model: 'VR-X712U-0013'}, {vendor: 'TUYATEC', model: 'GDKES-02TZXD'},
2768
2768
  {vendor: 'Earda', model: 'ESW-2ZAA-EU'}],
2769
- extend: tuya.extend.switch(),
2770
- exposes: [e.switch().withEndpoint('left'), e.switch().withEndpoint('right')],
2769
+ extend: tuya.extend.switch({backlightModeOffNormalInverted: true, endpoints: ['left', 'right']}),
2771
2770
  endpoint: (device) => {
2772
2771
  return {'left': 1, 'right': 2};
2773
2772
  },
@@ -2806,7 +2805,7 @@ module.exports = [
2806
2805
  model: 'TS0013',
2807
2806
  vendor: 'TuYa',
2808
2807
  description: 'Smart light switch - 3 gang without neutral wire',
2809
- extend: tuya.extend.switch({backlightMode: true, endpoints: ['left', 'center', 'right']}),
2808
+ extend: tuya.extend.switch({backlightModeLowMediumHigh: true, endpoints: ['left', 'center', 'right']}),
2810
2809
  endpoint: (device) => {
2811
2810
  return {'left': 1, 'center': 2, 'right': 3};
2812
2811
  },
@@ -2857,7 +2856,7 @@ module.exports = [
2857
2856
  model: 'TS0014',
2858
2857
  vendor: 'TuYa',
2859
2858
  description: 'Smart light switch - 4 gang without neutral wire',
2860
- extend: tuya.extend.switch({backlightMode: true, endpoints: ['l1', 'l2', 'l3', 'l4']}),
2859
+ extend: tuya.extend.switch({backlightModeLowMediumHigh: true, endpoints: ['l1', 'l2', 'l3', 'l4']}),
2861
2860
  endpoint: (device) => {
2862
2861
  return {'l1': 1, 'l2': 2, 'l3': 3, 'l4': 4};
2863
2862
  },
@@ -36,9 +36,14 @@ module.exports = [
36
36
  model: 'MCT-350 SMA',
37
37
  vendor: 'Visonic',
38
38
  description: 'Magnetic door & window contact sensor',
39
- fromZigbee: [fz.ias_contact_alarm_1],
39
+ fromZigbee: [fz.ias_contact_alarm_1, fz.temperature],
40
40
  toZigbee: [],
41
- exposes: [e.contact(), e.battery_low(), e.tamper()],
41
+ configure: async (device, coordinatorEndpoint, logger) => {
42
+ const endpoint = device.getEndpoint(1);
43
+ await reporting.bind(endpoint, coordinatorEndpoint, ['msTemperatureMeasurement']);
44
+ await reporting.temperature(endpoint);
45
+ },
46
+ exposes: [e.contact(), e.battery_low(), e.tamper(), e.temperature()],
42
47
  },
43
48
  {
44
49
  zigbeeModel: ['MCT-340 E'],
package/lib/tuya.js CHANGED
@@ -1158,7 +1158,10 @@ const tuyaExposes = {
1158
1158
  powerFactorWithPhase: (phase) => exposes.numeric(`power_factor_${phase}`, ea.STATE).withUnit('%')
1159
1159
  .withDescription(`Instantaneous measured power factor (phase ${phase.toUpperCase()})`),
1160
1160
  switchType: () => exposes.enum('switch_type', ea.ALL, ['toggle', 'state', 'momentary']).withDescription('Type of the switch'),
1161
- backlightMode: () => exposes.enum('backlight_mode', ea.ALL, ['low', 'medium', 'high']).withDescription('Intensity of the backlight'),
1161
+ backlightModeLowMediumHigh: () => exposes.enum('backlight_mode', ea.ALL, ['low', 'medium', 'high'])
1162
+ .withDescription('Intensity of the backlight'),
1163
+ backlightModeOffNormalInverted: () => exposes.enum('backlight_mode', ea.ALL, ['off', 'normal', 'inverted'])
1164
+ .withDescription('Mode of the backlight'),
1162
1165
  indicatorMode: () => exposes.enum('indicator_mode', ea.ALL, ['off', 'off/on', 'on/off', 'on']).withDescription('LED indicator mode'),
1163
1166
  powerOutageMemory: () => exposes.enum('power_outage_memory', ea.ALL, ['on', 'off', 'restore'])
1164
1167
  .withDescription('Recover state after power outage'),
@@ -1546,7 +1549,8 @@ const tuyaTz = {
1546
1549
  backlight_indicator_mode: {
1547
1550
  key: ['backlight_mode', 'indicator_mode'],
1548
1551
  convertSet: async (entity, key, value, meta) => {
1549
- const lookup = key === 'backlight_mode' ? {'low': 0, 'medium': 1, 'high': 2} : {'off': 0, 'off/on': 1, 'on/off': 2, 'on': 3};
1552
+ const lookup = key === 'backlight_mode' ? {'low': 0, 'medium': 1, 'high': 2, 'off': 0, 'normal': 1, 'inverted': 2} :
1553
+ {'off': 0, 'off/on': 1, 'on/off': 2, 'on': 3};
1550
1554
  value = value.toLowerCase();
1551
1555
  utils.validateValue(value, Object.keys(lookup));
1552
1556
  await entity.write('genOnOff', {tuyaBacklightMode: lookup[value]});
@@ -1644,7 +1648,7 @@ const tuyaFz = {
1644
1648
  }
1645
1649
  },
1646
1650
  },
1647
- backlight_mode: {
1651
+ backlight_mode_low_medium_high: {
1648
1652
  cluster: 'genOnOff',
1649
1653
  type: ['attributeReport', 'readResponse'],
1650
1654
  convert: (model, msg, publish, options, meta) => {
@@ -1655,6 +1659,17 @@ const tuyaFz = {
1655
1659
  }
1656
1660
  },
1657
1661
  },
1662
+ backlight_mode_off_normal_inverted: {
1663
+ cluster: 'genOnOff',
1664
+ type: ['attributeReport', 'readResponse'],
1665
+ convert: (model, msg, publish, options, meta) => {
1666
+ if (msg.data.hasOwnProperty('tuyaBacklightMode')) {
1667
+ const value = msg.data['tuyaBacklightMode'];
1668
+ const backlightLookup = {0: 'off', 1: 'normal', 2: 'inverted'};
1669
+ return {backlight_mode: backlightLookup[value]};
1670
+ }
1671
+ },
1672
+ },
1658
1673
  indicator_mode: {
1659
1674
  cluster: 'genOnOff',
1660
1675
  type: ['attributeReport', 'readResponse'],
@@ -1747,8 +1762,19 @@ const tuyaExtend = {
1747
1762
  toZigbee.push(tuyaTz.switch_type);
1748
1763
  exposes.push(tuyaExposes.switchType());
1749
1764
  }
1750
- if (options.backlightMode || options.indicatorMode) {
1751
- fromZigbee.push(options.backlightMode ? tuyaFz.backlight_mode : tuyaFz.indicator_mode);
1765
+ if (options.backlightModeLowMediumHigh || options.backlightModeOffNormalInverted || options.indicatorMode) {
1766
+ if (options.backlightModeLowMediumHigh) {
1767
+ fromZigbee.push(tuyaFz.backlight_mode_low_medium_high);
1768
+ exposes.push(tuyaExposes.backlightModeLowMediumHigh());
1769
+ }
1770
+ if (options.backlightModeOffNormalInverted) {
1771
+ fromZigbee.push(tuyaFz.backlight_mode_off_normal_inverted);
1772
+ exposes.push(tuyaExposes.backlightModeOffNormalInverted());
1773
+ }
1774
+ if (options.indicatorMode) {
1775
+ fromZigbee.push(tuyaFz.indicator_mode);
1776
+ exposes.push(tuyaExposes.indicatorMode());
1777
+ }
1752
1778
  toZigbee.push(tuyaTz.backlight_indicator_mode);
1753
1779
  exposes.push(options.backlightMode ? tuyaExposes.backlightMode() : tuyaExposes.indicatorMode());
1754
1780
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "15.0.4",
3
+ "version": "15.0.5",
4
4
  "description": "Collection of device converters to be used with zigbee-herdsman",
5
5
  "main": "index.js",
6
6
  "files": [