zigbee-herdsman-converters 14.0.590 → 14.0.593

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.
package/README.md CHANGED
@@ -23,6 +23,7 @@ If any of those commands finish with an error your PR won't pass the tests and w
23
23
  - `supportsHueAndSaturation`: see toZigbee.light_color (default: false)
24
24
  - `timeout`: timeout for commands to this device used in toZigbee. (default: 10000)
25
25
  - `coverInverted`: Set to true for cover controls that report position=100 as open (default: false)
26
+ - `coverStateFromTilt`: Set cover state based on tilt
26
27
  - `turnsOffAtBrightness1`: Indicates light turns off when brightness 1 is set (default: false)
27
28
  - `pinCodeCount`: Amount of pincodes the lock can handle
28
29
  - `disableActionGroup`: Prevents some converters adding the action_group to the payload (default: false)
@@ -1542,15 +1542,22 @@ const converters = {
1542
1542
  // For zigbee-herdsman-converters: open = 100, close = 0
1543
1543
  // ubisys J1 will report 255 if lift or tilt positions are not known, so skip that.
1544
1544
  const invert = model.meta && model.meta.coverInverted ? !options.invert_cover : options.invert_cover;
1545
+ const coverStateFromTilt = model.meta && model.meta.coverStateFromTilt;
1545
1546
  if (msg.data.hasOwnProperty('currentPositionLiftPercentage') && msg.data['currentPositionLiftPercentage'] <= 100) {
1546
1547
  const value = msg.data['currentPositionLiftPercentage'];
1547
1548
  result[postfixWithEndpointName('position', msg, model, meta)] = invert ? value : 100 - value;
1548
- result[postfixWithEndpointName('state', msg, model, meta)] =
1549
- invert ? (value === 100 ? 'CLOSE' : 'OPEN') : (value === 0 ? 'CLOSE' : 'OPEN');
1549
+ if (!coverStateFromTilt) {
1550
+ result[postfixWithEndpointName('state', msg, model, meta)] =
1551
+ invert ? (value === 100 ? 'CLOSE' : 'OPEN') : (value === 0 ? 'CLOSE' : 'OPEN');
1552
+ }
1550
1553
  }
1551
1554
  if (msg.data.hasOwnProperty('currentPositionTiltPercentage') && msg.data['currentPositionTiltPercentage'] <= 100) {
1552
1555
  const value = msg.data['currentPositionTiltPercentage'];
1553
1556
  result[postfixWithEndpointName('tilt', msg, model, meta)] = invert ? value : 100 - value;
1557
+ if (coverStateFromTilt) {
1558
+ result[postfixWithEndpointName('state', msg, model, meta)] =
1559
+ invert ? (value === 100 ? 'OPEN' : 'CLOSE') : (value === 0 ? 'OPEN' : 'CLOSE');
1560
+ }
1554
1561
  }
1555
1562
  return result;
1556
1563
  },
@@ -6820,6 +6827,17 @@ const converters = {
6820
6827
  case 7: {
6821
6828
  return {battery: value};
6822
6829
  }
6830
+ case 10: {
6831
+ let data = 'disabled';
6832
+ if (value == 1) {
6833
+ data = '24h';
6834
+ } else if (value == 2) {
6835
+ data = '48h';
6836
+ } else if (value == 3) {
6837
+ data = '72h';
6838
+ }
6839
+ return {weather_delay: data};
6840
+ }
6823
6841
  case 11: {
6824
6842
  // value reported in seconds
6825
6843
  return {timer_time_left: value / 60};
@@ -6833,6 +6851,43 @@ const converters = {
6833
6851
  // value reported in seconds
6834
6852
  return {last_valve_open_duration: value / 60};
6835
6853
  }
6854
+ case 16: {
6855
+ const tresult = {
6856
+ cycle_timer_1: '',
6857
+ cycle_timer_2: '',
6858
+ cycle_timer_3: '',
6859
+ cycle_timer_4: '',
6860
+ };
6861
+ for (let index = 0; index < 40; index += 12) {
6862
+ const timer = tuya.convertRawToCycleTimer(value.slice(index));
6863
+ if (timer.irrigationDuration > 0) {
6864
+ tresult['cycle_timer_' + (index / 13 + 1)] = timer.starttime +
6865
+ ' / ' + timer.endtime + ' / ' +
6866
+ timer.irrigationDuration + ' / ' +
6867
+ timer.pauseDuration + ' / ' +
6868
+ timer.weekdays + ' / ' + timer.active;
6869
+ }
6870
+ }
6871
+ return tresult;
6872
+ }
6873
+ case 17: {
6874
+ const tresult = {
6875
+ normal_schedule_timer_1: '',
6876
+ normal_schedule_timer_2: '',
6877
+ normal_schedule_timer_3: '',
6878
+ normal_schedule_timer_4: '',
6879
+ };
6880
+ for (let index = 0; index < 40; index += 13) {
6881
+ const timer = tuya.convertRawToTimer(value.slice(index));
6882
+ if (timer.duration > 0) {
6883
+ tresult['normal_schedule_timer_' + (index / 13 + 1)] = timer.time +
6884
+ ' / ' + timer.duration +
6885
+ ' / ' + timer.weekdays +
6886
+ ' / ' + timer.active;
6887
+ }
6888
+ }
6889
+ return tresult;
6890
+ }
6836
6891
  default: {
6837
6892
  meta.logger.warn(`zigbee-herdsman-converters:RTXZVG1Valve: NOT RECOGNIZED DP ` +
6838
6893
  `#${dp} with data ${JSON.stringify(dpValue)}`);
@@ -3992,14 +3992,132 @@ const converters = {
3992
3992
  return {state: {timer: value}};
3993
3993
  },
3994
3994
  },
3995
- ZVG1_timer_state: {
3996
- key: ['timer_state'],
3997
- convertSet: async (entity, key, value, meta) => {
3998
- let timerState = 2;
3999
- if (value === 'disabled') timerState = 0;
4000
- else if (value === 'active') timerState = 1;
4001
- await tuya.sendDataPointValue(entity, 11, timerState, 'dataRequest', 1);
4002
- return {state: {timer_state: value}};
3995
+ ZVG1_weather_delay: {
3996
+ key: ['weather_delay'],
3997
+ convertSet: async (entity, key, value, meta) => {
3998
+ const lookup = {'disabled': 0, '24h': 1, '48h': 2, '72h': 3};
3999
+ await tuya.sendDataPointEnum(entity, 10, lookup[value]);
4000
+ },
4001
+ },
4002
+ ZVG1_cycle_timer: {
4003
+ key: ['cycle_timer_1', 'cycle_timer_2', 'cycle_timer_3', 'cycle_timer_4'],
4004
+ convertSet: async (entity, key, value, meta) => {
4005
+ let data = [0];
4006
+ const footer = [0x64];
4007
+ if (value == '') {
4008
+ // delete
4009
+ data.push(0x04);
4010
+ data.push(parseInt(key.substr(-1)));
4011
+ await tuya.sendDataPointRaw(entity, 16, data);
4012
+ const ret = {state: {}};
4013
+ ret['state'][key] = value;
4014
+ return ret;
4015
+ } else {
4016
+ if ((meta.state.hasOwnProperty(key) && meta.state[key] == '') ||
4017
+ !meta.state.hasOwnProperty(key)) {
4018
+ data.push(0x03);
4019
+ } else {
4020
+ data.push(0x02);
4021
+ data.push(parseInt(key.substr(-1)));
4022
+ }
4023
+ }
4024
+
4025
+ const tarray = value.replace(/ /g, '').split('/');
4026
+ if (tarray.length < 4) {
4027
+ throw new Error('Please check the format of the timer string');
4028
+ }
4029
+ if (tarray.length < 5) {
4030
+ tarray.push('MoTuWeThFrSaSu');
4031
+ }
4032
+
4033
+ if (tarray.length < 6) {
4034
+ tarray.push('1');
4035
+ }
4036
+
4037
+ const starttime = tarray[0];
4038
+ const endtime = tarray[1];
4039
+ const irrigationDuration = tarray[2];
4040
+ const pauseDuration = tarray[3];
4041
+ const weekdays = tarray[4];
4042
+ const active = parseInt(tarray[5]);
4043
+
4044
+ if (!(active == 0 || active == 1)) {
4045
+ throw new Error('Active value only 0 or 1 allowed');
4046
+ }
4047
+ data.push(active);
4048
+
4049
+ const weekdaysPart = tuya.convertWeekdaysTo1ByteHexArray(weekdays);
4050
+ data = data.concat(weekdaysPart);
4051
+
4052
+ data = data.concat(tuya.convertTimeTo2ByteHexArray(starttime));
4053
+ data = data.concat(tuya.convertTimeTo2ByteHexArray(endtime));
4054
+
4055
+ data = data.concat(tuya.convertDecimalValueTo2ByteHexArray(irrigationDuration));
4056
+ data = data.concat(tuya.convertDecimalValueTo2ByteHexArray(pauseDuration));
4057
+
4058
+ data = data.concat(footer);
4059
+ await tuya.sendDataPointRaw(entity, 16, data);
4060
+ const ret = {state: {}};
4061
+ ret['state'][key] = value;
4062
+ return ret;
4063
+ },
4064
+ },
4065
+ ZVG1_normal_schedule_timer: {
4066
+ key: ['normal_schedule_timer_1', 'normal_schedule_timer_2', 'normal_schedule_timer_3', 'normal_schedule_timer_4'],
4067
+ convertSet: async (entity, key, value, meta) => {
4068
+ let data = [0];
4069
+ const footer = [0x07, 0xe6, 0x08, 0x01, 0x01];
4070
+ if (value == '') {
4071
+ // delete
4072
+ data.push(0x04);
4073
+ data.push(parseInt(key.substr(-1)));
4074
+ await tuya.sendDataPointRaw(entity, 17, data);
4075
+ const ret = {state: {}};
4076
+ ret['state'][key] = value;
4077
+ return ret;
4078
+ } else {
4079
+ if ((meta.state.hasOwnProperty(key) && meta.state[key] == '') || !meta.state.hasOwnProperty(key)) {
4080
+ data.push(0x03);
4081
+ } else {
4082
+ data.push(0x02);
4083
+ data.push(parseInt(key.substr(-1)));
4084
+ }
4085
+ }
4086
+
4087
+ const tarray = value.replace(/ /g, '').split('/');
4088
+ if (tarray.length < 2) {
4089
+ throw new Error('Please check the format of the timer string');
4090
+ }
4091
+ if (tarray.length < 3) {
4092
+ tarray.push('MoTuWeThFrSaSu');
4093
+ }
4094
+
4095
+ if (tarray.length < 4) {
4096
+ tarray.push('1');
4097
+ }
4098
+
4099
+ const time = tarray[0];
4100
+ const duration = tarray[1];
4101
+ const weekdays = tarray[2];
4102
+ const active = parseInt(tarray[3]);
4103
+
4104
+ if (!(active == 0 || active == 1)) {
4105
+ throw new Error('Active value only 0 or 1 allowed');
4106
+ }
4107
+
4108
+ data = data.concat(tuya.convertTimeTo2ByteHexArray(time));
4109
+
4110
+ const durationPart = tuya.convertDecimalValueTo2ByteHexArray(duration);
4111
+ data = data.concat(durationPart);
4112
+
4113
+ const weekdaysPart = tuya.convertWeekdaysTo1ByteHexArray(weekdays);
4114
+ data = data.concat(weekdaysPart);
4115
+ data = data.concat([64, active]);
4116
+ data = data.concat(footer);
4117
+ await tuya.sendDataPointRaw(entity, 17, data);
4118
+ const ret = {state: {}};
4119
+ ret['state'][key] = value;
4120
+ return ret;
4003
4121
  },
4004
4122
  },
4005
4123
  EMIZB_132_mode: {
@@ -12,6 +12,7 @@ module.exports = [
12
12
  description: 'Gen. 2 hybrid E-Wand',
13
13
  fromZigbee: [fz.cover_position_tilt, fz.battery],
14
14
  toZigbee: [tz.cover_state, tz.cover_position_tilt],
15
+ meta: {coverStateFromTilt: true},
15
16
  configure: async (device, coordinatorEndpoint, logger) => {
16
17
  const endpoint = device.getEndpoint(1);
17
18
  await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'closuresWindowCovering']);
package/devices/gewiss.js CHANGED
@@ -39,8 +39,8 @@ module.exports = [
39
39
  toZigbee: [tz.cover_state, tz.cover_position_tilt],
40
40
  configure: async (device, coordinatorEndpoint, logger) => {
41
41
  const endpoint = device.getEndpoint(1);
42
- await reporting.bind(endpoint, coordinatorEndpoint, ['genLevelCtrl']);
43
- await reporting.brightness(endpoint);
42
+ await reporting.bind(endpoint, coordinatorEndpoint, ['closuresWindowCovering']);
43
+ await reporting.currentPositionLiftPercentage(endpoint);
44
44
  },
45
45
  exposes: [e.cover_position()],
46
46
  },
package/devices/giex.js CHANGED
@@ -104,7 +104,10 @@ const tzLocal = {
104
104
 
105
105
  module.exports = [
106
106
  {
107
- fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_sh1btabb'}],
107
+ fingerprint: [
108
+ {modelID: 'TS0601', manufacturerName: '_TZE200_sh1btabb'},
109
+ {modelID: 'TS0601', manufacturerName: '_TZE200_a7sghmms'},
110
+ ],
108
111
  model: 'QT06',
109
112
  vendor: 'GiEX',
110
113
  description: 'Water irrigation valve',
@@ -330,6 +330,13 @@ module.exports = [
330
330
  description: 'Zigbee smart filament LED bulb',
331
331
  extend: extend.light_onoff_brightness_colortemp_color({colorTempRange: [158, 495]}),
332
332
  },
333
+ {
334
+ zigbeeModel: ['GL-S-006P'],
335
+ model: 'GL-S-006P',
336
+ vendor: 'Gledopto',
337
+ description: 'Zigbee GU10 LED lamp',
338
+ extend: extend.light_onoff_brightness_colortemp_color({colorTempRange: [158, 495]}),
339
+ },
333
340
  {
334
341
  zigbeeModel: ['GL-MC-001P'],
335
342
  model: 'GL-MC-001P',
package/devices/ikea.js CHANGED
@@ -319,6 +319,13 @@ module.exports = [
319
319
  description: 'TRADFRI LED bulb E27 1000 lumen, dimmable, opal white',
320
320
  extend: tradfriExtend.light_onoff_brightness(),
321
321
  },
322
+ {
323
+ zigbeeModel: ['TRADFRIbulbE26WWglobeclear250lm'],
324
+ model: 'LED2008G3',
325
+ vendor: 'IKEA',
326
+ description: 'TRADFRI LED bulb E26 250 lumen, wireless dimmable warm white/globe clear',
327
+ extend: tradfriExtend.light_onoff_brightness(),
328
+ },
322
329
  {
323
330
  zigbeeModel: ['TRADFRIbulbG125E27WSopal470lm', 'TRADFRIbulbG125E26WSopal450lm'],
324
331
  model: 'LED1936G5',
package/devices/lidl.js CHANGED
@@ -561,6 +561,14 @@ module.exports = [
561
561
  device.getEndpoint(1).saveClusterAttributeKeyValue('lightingColorCtrl', {colorCapabilities: 29});
562
562
  },
563
563
  },
564
+ {
565
+ fingerprint: [{modelID: 'TS0505B', manufacturerName: '_TZ3210_zbabx9wh'}],
566
+ model: 'HG08007',
567
+ vendor: 'TuYa',
568
+ description: 'Livarno Home outdoor LED band',
569
+ extend: extend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500], disableColorTempStartup: true}),
570
+ meta: {applyRedFix: true, enhancedHue: false},
571
+ },
564
572
  {
565
573
  fingerprint: [{modelID: 'TS0505B', manufacturerName: '_TZ3210_z1vlyufu'}],
566
574
  model: '14158704L',
package/devices/osram.js CHANGED
@@ -331,7 +331,8 @@ module.exports = [
331
331
  fz.legacy.osram_lightify_switch_cmdOff, fz.legacy.osram_lightify_switch_cmdMove, fz.battery,
332
332
  fz.legacy.osram_lightify_switch_cmdMoveToLevelWithOnOff],
333
333
  exposes: [e.battery(), e.action([
334
- 'up', 'up_hold', 'up_release', 'down_release', 'circle_release', 'circle_hold', 'down', 'down_hold', 'circle_click'])],
334
+ 'on', 'brightness_move_up', 'brightness_move_down', 'brightness_stop', 'color_temperature_move', 'hue_move', 'hue_stop',
335
+ 'move_to_saturation', 'off', 'brightness_move_to_level'])],
335
336
  toZigbee: [],
336
337
  meta: {battery: {voltageToPercentage: '3V_2500'}},
337
338
  ota: ota.ledvance,
@@ -1325,7 +1325,7 @@ module.exports = [
1325
1325
  extend: hueExtend.light_onoff_brightness_colortemp(),
1326
1326
  },
1327
1327
  {
1328
- zigbeeModel: ['LTP002'],
1328
+ zigbeeModel: ['LTP002', '929003054901'],
1329
1329
  model: '4023330P7',
1330
1330
  vendor: 'Philips',
1331
1331
  description: 'Hue white ambiance suspension Amaze',
package/devices/rtx.js CHANGED
@@ -6,22 +6,74 @@ const ea = exposes.access;
6
6
 
7
7
  module.exports = [
8
8
  {
9
- fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_akjefhj5'}, {modelID: 'TS0601', manufacturerName: '_TZE200_2wg5qrjy'},
10
- {modelID: 'TS0601', manufacturerName: '_TZE200_81isopgh'}],
9
+ fingerprint: [
10
+ {modelID: 'TS0601', manufacturerName: '_TZE200_akjefhj5'},
11
+ {modelID: 'TS0601', manufacturerName: '_TZE200_2wg5qrjy'},
12
+ {modelID: 'TS0601', manufacturerName: '_TZE200_81isopgh'},
13
+ ],
11
14
  model: 'ZVG1',
12
15
  vendor: 'RTX',
13
16
  description: 'Zigbee smart water valve',
14
17
  fromZigbee: [fz.ZVG1, fz.ignore_tuya_set_time, fz.ignore_basic_report],
15
- toZigbee: [tz.tuya_switch_state, tz.ZVG1_timer, tz.ZVG1_timer_state],
18
+ toZigbee: [tz.tuya_switch_state, tz.ZVG1_weather_delay, tz.ZVG1_timer, tz.ZVG1_cycle_timer, tz.ZVG1_normal_schedule_timer],
16
19
  exposes: [e.switch().setAccess('state', ea.STATE_SET), e.battery(),
17
- exposes.enum('timer_state', ea.STATE_SET, ['disabled', 'active', 'enabled']),
18
- exposes.numeric('timer', exposes.access.STATE_SET).withValueMin(0).withValueMax(240).withUnit('min')
20
+ exposes.enum('weather_delay', ea.STATE_SET, ['disabled', '24h', '48h', '72h']),
21
+ exposes.enum('timer_state', ea.STATE, ['disabled', 'active', 'enabled']),
22
+ exposes.numeric('timer', ea.STATE_SET).withValueMin(0).withValueMax(60).withUnit('min')
19
23
  .withDescription('Auto off after specific time'),
20
- exposes.numeric('timer_time_left', exposes.access.STATE).withUnit('min')
24
+ exposes.numeric('timer_time_left', ea.STATE).withUnit('min')
21
25
  .withDescription('Auto off timer time left'),
22
- exposes.numeric('last_valve_open_duration', exposes.access.STATE).withUnit('min')
26
+ exposes.numeric('last_valve_open_duration', ea.STATE).withUnit('min')
23
27
  .withDescription('Time the valve was open when state on'),
24
- exposes.numeric('water_consumed', exposes.access.STATE).withUnit('l')
25
- .withDescription('Liters of water consumed')],
28
+ exposes.numeric('water_consumed', ea.STATE).withUnit('l')
29
+ .withDescription('Liters of water consumed'),
30
+ exposes.text('cycle_timer_1', ea.STATE_SET).withDescription('Format 08:00 / 20:00 / 15 / 60 / MoTuWeThFrSaSu / 1 (' +
31
+ '08:00 = start time ' +
32
+ '20:00 = end time ' +
33
+ '15 = irrigation duration in minutes ' +
34
+ '60 = pause duration in minutes ' +
35
+ 'MoTu..= active weekdays ' +
36
+ '1 = deactivate timer with 0)'),
37
+ exposes.text('cycle_timer_2', ea.STATE_SET).withDescription('Format 08:00 / 20:00 / 15 / 60 / MoTuWeThFrSaSu / 1 (' +
38
+ '08:00 = start time ' +
39
+ '20:00 = end time ' +
40
+ '15 = irrigation duration in minutes ' +
41
+ '60 = pause duration in minutes ' +
42
+ 'MoTu..= active weekdays ' +
43
+ '1 = deactivate timer with 0)'),
44
+ exposes.text('cycle_timer_3', ea.STATE_SET).withDescription('Format 08:00 / 20:00 / 15 / 60 / MoTuWeThFrSaSu / 1 (' +
45
+ '08:00 = start time ' +
46
+ '20:00 = end time ' +
47
+ '15 = irrigation duration in minutes ' +
48
+ '60 = pause duration in minutes ' +
49
+ 'MoTu..= active weekdays ' +
50
+ '1 = deactivate timer with 0)'),
51
+ exposes.text('cycle_timer_4', ea.STATE_SET).withDescription('Format 08:00 / 20:00 / 15 / 60 / MoTuWeThFrSaSu / 1 (' +
52
+ '08:00 = start time ' +
53
+ '20:00 = end time ' +
54
+ '15 = irrigation duration in minutes ' +
55
+ '60 = pause duration in minutes ' +
56
+ 'MoTu..= active weekdays ' +
57
+ '1 = deactivate timer with 0)'),
58
+ exposes.text('normal_schedule_timer_1', ea.STATE_SET).withDescription('Format 08:00 / 15 / MoTuWeThFrSaSu / 1 (' +
59
+ '08:00 = start time ' +
60
+ '15 = duration in minutes ' +
61
+ 'MoTu..= active weekdays ' +
62
+ '1 = deactivate timer with 0)'),
63
+ exposes.text('normal_schedule_timer_2', ea.STATE_SET).withDescription('Format 08:00 / 15 / MoTuWeThFrSaSu / 1 (' +
64
+ '08:00 = start time ' +
65
+ '15 = duration in minutes ' +
66
+ 'MoTu..= active weekdays ' +
67
+ '1 = deactivate timer with 0)'),
68
+ exposes.text('normal_schedule_timer_3', ea.STATE_SET).withDescription('Format 08:00 / 15 / MoTuWeThFrSaSu / 1 (' +
69
+ '08:00 = start time ' +
70
+ '15 = duration in minutes ' +
71
+ 'MoTu..= active weekdays ' +
72
+ '1 = deactivate timer with 0)'),
73
+ exposes.text('normal_schedule_timer_4', ea.STATE_SET).withDescription('Format 08:00 / 15 / MoTuWeThFrSaSu / 1 (' +
74
+ '08:00 = start time ' +
75
+ '15 = duration in minutes ' +
76
+ 'MoTu..= active weekdays ' +
77
+ '1 = deactivate timer with 0)')],
26
78
  },
27
79
  ];
@@ -1,6 +1,7 @@
1
1
  const exposes = require('../lib/exposes');
2
2
  const fz = {...require('../converters/fromZigbee'), legacy: require('../lib/legacy').fromZigbee};
3
3
  const tz = require('../converters/toZigbee');
4
+ const extend = require('../lib/extend');
4
5
  const ea = exposes.access;
5
6
 
6
7
  module.exports = [
@@ -49,6 +50,14 @@ module.exports = [
49
50
  ea.STATE_SET).withColor(['hs']),
50
51
  ],
51
52
  },
53
+ {
54
+ fingerprint: [{modelID: 'TS0503B', manufacturerName: '_TZB210_zdvrsts8'}],
55
+ model: 'WZ5_rgb_1',
56
+ vendor: 'TuYa',
57
+ description: 'Zigbee & RF 5 in 1 LED controller (RGB mode)',
58
+ extend: extend.light_onoff_brightness_color({supportsHS: true, preferHS: true, disableEffect: true}),
59
+ meta: {applyRedFix: true, enhancedHue: false},
60
+ },
52
61
  {
53
62
  fingerprint: [
54
63
  {modelID: 'TS0601', manufacturerName: '_TZE200_3thxjahu'},
package/devices/tuya.js CHANGED
@@ -19,7 +19,8 @@ const TS011Fplugs = ['_TZ3000_5f43h46b', '_TZ3000_cphmq0q7', '_TZ3000_dpo1ysak',
19
19
  '_TZ3000_jvzvulen', '_TZ3000_mraovvmm', '_TZ3000_nfnmi125', '_TZ3000_ps3dmato', '_TZ3000_w0qqde0g', '_TZ3000_u5u4cakc',
20
20
  '_TZ3000_rdtixbnu', '_TZ3000_typdpbpg', '_TZ3000_kx0pris5', '_TZ3000_amdymr7l', '_TZ3000_z1pnpsdo', '_TZ3000_ksw8qtmt',
21
21
  '_TZ3000_1h2x4akh', '_TZ3000_9vo5icau', '_TZ3000_cehuw1lw', '_TZ3000_ko6v90pg', '_TZ3000_f1bapcit', '_TZ3000_cjrngdr3',
22
- '_TZ3000_zloso4jk', '_TZ3000_r6buo8ba', '_TZ3000_iksasdbv', '_TZ3000_idrffznf', '_TZ3000_okaz9tjs', '_TZ3210_q7oryllx'];
22
+ '_TZ3000_zloso4jk', '_TZ3000_r6buo8ba', '_TZ3000_iksasdbv', '_TZ3000_idrffznf', '_TZ3000_okaz9tjs', '_TZ3210_q7oryllx',
23
+ '_TZ3000_ss98ec5d', '_TZ3000_gznh2xla'];
23
24
 
24
25
  const tzLocal = {
25
26
  TS0504B_color: {
@@ -796,7 +797,8 @@ module.exports = [
796
797
  {modelID: 'TS0503B', manufacturerName: '_TZ3210_trm3l2aw'},
797
798
  {modelID: 'TS0503B', manufacturerName: '_TZ3210_95txyzbx'},
798
799
  {modelID: 'TS0503B', manufacturerName: '_TZ3210_odlghna1'},
799
- {modelID: 'TS0503B', manufacturerName: '_TZ3220_wp1k8xws'}],
800
+ {modelID: 'TS0503B', manufacturerName: '_TZ3220_wp1k8xws'},
801
+ {modelID: 'TS0503B', manufacturerName: '_TZ3210_wp1k8xws'}],
800
802
  model: 'TS0503B',
801
803
  vendor: 'TuYa',
802
804
  description: 'Zigbee RGB light',
@@ -914,6 +916,7 @@ module.exports = [
914
916
  {
915
917
  fingerprint: [{modelID: 'TS0207', manufacturerName: '_TZ3000_m0vaazab'},
916
918
  {modelID: 'TS0207', manufacturerName: '_TZ3000_ufttklsz'},
919
+ {modelID: 'TS0207', manufacturerName: '_TZ3000_nkkl7uzv'},
917
920
  {modelID: 'TS0207', manufacturerName: '_TZ3000_5k5vh43t'}],
918
921
  model: 'TS0207_repeater',
919
922
  vendor: 'TuYa',
package/devices/xiaomi.js CHANGED
@@ -1007,6 +1007,26 @@ module.exports = [
1007
1007
  },
1008
1008
  ota: ota.zigbeeOTA,
1009
1009
  },
1010
+ {
1011
+ zigbeeModel: ['lumi.motion.acn001'],
1012
+ model: 'RTCGQ15LM',
1013
+ vendor: 'Xiaomi',
1014
+ description: 'Aqara E1 human body movement and illuminance sensor',
1015
+ fromZigbee: [fz.aqara_occupancy_illuminance, fz.aqara_opple, fz.battery],
1016
+ toZigbee: [tz.aqara_detection_interval],
1017
+ exposes: [e.occupancy(), e.illuminance_lux().withProperty('illuminance'),
1018
+ e.illuminance().withUnit('lx').withDescription('Measured illuminance in lux'),
1019
+ exposes.numeric('detection_interval', ea.ALL).withValueMin(2).withValueMax(65535).withUnit('s')
1020
+ .withDescription('Time interval for detecting actions'),
1021
+ e.device_temperature(), e.battery(), e.battery_voltage(), e.power_outage_count(false)],
1022
+ meta: {battery: {voltageToPercentage: '3V_2850_3200'}},
1023
+ configure: async (device, coordinatorEndpoint, logger) => {
1024
+ const endpoint = device.getEndpoint(1);
1025
+ await endpoint.read('genPowerCfg', ['batteryVoltage']);
1026
+ await endpoint.read('aqaraOpple', [0x0102], {manufacturerCode: 0x115f});
1027
+ },
1028
+ ota: ota.zigbeeOTA,
1029
+ },
1010
1030
  {
1011
1031
  zigbeeModel: ['lumi.motion.ac01'],
1012
1032
  model: 'RTCZCGQ11LM',
package/lib/tuya.js CHANGED
@@ -113,6 +113,116 @@ function logUnexpectedDataValue(where, msg, dpValue, meta, valueKind, expectedMi
113
113
  }
114
114
  }
115
115
 
116
+ function convertRawToCycleTimer(value) {
117
+ let timernr = 0;
118
+ let starttime = '00:00';
119
+ let endtime = '00:00';
120
+ let irrigationDuration = 0;
121
+ let pauseDuration = 0;
122
+ let weekdays = 'once';
123
+ let timeractive = 0;
124
+ if (value.length > 11) {
125
+ timernr = value[1];
126
+ timeractive = value[2];
127
+ if (value[3] > 0) {
128
+ weekdays = (value[3] & 0x40 ? 'Mo' : '') +
129
+ (value[3] & 0x20 ? 'Tu' : '') +
130
+ (value[3] & 0x10 ? 'We' : '') +
131
+ (value[3] & 0x08 ? 'Th' : '') +
132
+ (value[3] & 0x04 ? 'Fr' : '') +
133
+ (value[3] & 0x02 ? 'Sa' : '') +
134
+ (value[3] & 0x01 ? 'Su' : '');
135
+ } else {
136
+ weekdays = 'once';
137
+ }
138
+ let minsincemidnight = value[4] * 256 + value[5];
139
+ starttime = String(parseInt(minsincemidnight / 60)).padStart(2, '0') + ':' + String(minsincemidnight % 60).padStart(2, '0');
140
+ minsincemidnight = value[6] * 256 + value[7];
141
+ endtime = String(parseInt(minsincemidnight / 60)).padStart(2, '0') + ':' + String(minsincemidnight % 60).padStart(2, '0');
142
+ irrigationDuration = value[8] * 256 + value[9];
143
+ pauseDuration = value[10] * 256 + value[11];
144
+ }
145
+ return {
146
+ timernr: timernr,
147
+ starttime: starttime,
148
+ endtime: endtime,
149
+ irrigationDuration: irrigationDuration,
150
+ pauseDuration: pauseDuration,
151
+ weekdays: weekdays,
152
+ active: timeractive,
153
+ };
154
+ }
155
+
156
+ function convertRawToTimer(value) {
157
+ let timernr = 0;
158
+ let starttime = '00:00';
159
+ let duration = 0;
160
+ let weekdays = 'once';
161
+ let timeractive = '';
162
+ if (value.length > 12) {
163
+ timernr = value[1];
164
+ const minsincemidnight = value[2] * 256 + value[3];
165
+ starttime = String(parseInt(minsincemidnight / 60)).padStart(2, '0') + ':' + String(minsincemidnight % 60).padStart(2, '0');
166
+ duration = value[4] * 256 + value[5];
167
+ if (value[6] > 0) {
168
+ weekdays = (value[6] & 0x40 ? 'Mo' : '') +
169
+ (value[6] & 0x20 ? 'Tu' : '') +
170
+ (value[6] & 0x10 ? 'We' : '') +
171
+ (value[6] & 0x08 ? 'Th' : '') +
172
+ (value[6] & 0x04 ? 'Fr' : '') +
173
+ (value[6] & 0x02 ? 'Sa' : '') +
174
+ (value[6] & 0x01 ? 'Su' : '');
175
+ } else {
176
+ weekdays = 'once';
177
+ }
178
+ timeractive = value[8];
179
+ }
180
+ return {timernr: timernr, time: starttime, duration: duration, weekdays: weekdays, active: timeractive};
181
+ }
182
+
183
+ function convertTimeTo2ByteHexArray(time) {
184
+ const timeArray = time.split(':');
185
+ if (timeArray.length != 2) {
186
+ throw new Error('Time format incorrect');
187
+ }
188
+ const timeHour = parseInt(timeArray[0]);
189
+ const timeMinute = parseInt(timeArray[1]);
190
+
191
+ if (timeHour > 23 || timeMinute > 59) {
192
+ throw new Error('Time incorrect');
193
+ }
194
+ return convertDecimalValueTo2ByteHexArray(timeHour * 60 + timeMinute);
195
+ }
196
+
197
+ function convertWeekdaysTo1ByteHexArray(weekdays) {
198
+ let nr = 0;
199
+ if (weekdays == 'once') {
200
+ return nr;
201
+ }
202
+ if (weekdays.includes('Mo')) {
203
+ nr |= 0x40;
204
+ }
205
+ if (weekdays.includes('Tu')) {
206
+ nr |= 0x20;
207
+ }
208
+ if (weekdays.includes('We')) {
209
+ nr |= 0x10;
210
+ }
211
+ if (weekdays.includes('Th')) {
212
+ nr |= 0x08;
213
+ }
214
+ if (weekdays.includes('Fr')) {
215
+ nr |= 0x04;
216
+ }
217
+ if (weekdays.includes('Sa')) {
218
+ nr |= 0x02;
219
+ }
220
+ if (weekdays.includes('Su')) {
221
+ nr |= 0x01;
222
+ }
223
+ return [nr];
224
+ }
225
+
116
226
  function convertDecimalValueTo4ByteHexArray(value) {
117
227
  const hexValue = Number(value).toString(16).padStart(8, '0');
118
228
  const chunk1 = hexValue.substr(0, 2);
@@ -1039,6 +1149,10 @@ module.exports = {
1039
1149
  dpValueFromStringBuffer,
1040
1150
  dpValueFromRaw,
1041
1151
  dpValueFromBitmap,
1152
+ convertRawToCycleTimer,
1153
+ convertRawToTimer,
1154
+ convertTimeTo2ByteHexArray,
1155
+ convertWeekdaysTo1ByteHexArray,
1042
1156
  convertDecimalValueTo4ByteHexArray,
1043
1157
  convertDecimalValueTo2ByteHexArray,
1044
1158
  onEventSetTime,
package/lib/utils.js CHANGED
@@ -114,7 +114,10 @@ function addActionGroup(payload, msg, definition) {
114
114
 
115
115
  function postfixWithEndpointName(value, msg, definition, meta) {
116
116
  // Prevent breaking change https://github.com/Koenkk/zigbee2mqtt/issues/13451
117
- if (!meta) meta = {device: null};
117
+ if (!meta) {
118
+ meta.logger.warn(`No meta passed to postfixWithEndpointName, update your external converter!`);
119
+ meta = {device: null};
120
+ }
118
121
  if (definition.meta && definition.meta.multiEndpoint) {
119
122
  const endpointName = definition.hasOwnProperty('endpoint') ?
120
123
  getKey(definition.endpoint(meta.device), msg.endpoint.ID) : msg.endpoint.ID;
package/lib/xiaomi.js CHANGED
@@ -219,7 +219,8 @@ const numericAttributes2Payload = (msg, meta, model, options, dataObject) => {
219
219
  payload[`state_${mapping}`] = value === 1 ? 'ON' : 'OFF';
220
220
  } else if (['WXKG14LM', 'WXKG16LM', 'WXKG17LM'].includes(model.model)) {
221
221
  payload.click_mode = {1: 'fast', 2: 'multi'}[value];
222
- } else if (['WXCJKG11LM', 'WXCJKG12LM', 'WXCJKG13LM', 'ZNMS12LM', 'RTCGQ14LM', 'ZNCLBL01LM'].includes(model.model)) {
222
+ } else if (['WXCJKG11LM', 'WXCJKG12LM', 'WXCJKG13LM', 'ZNMS12LM', 'ZNCLBL01LM', 'RTCGQ12LM', 'RTCGQ13LM', 'RTCGQ14LM',
223
+ 'RTCGQ15LM'].includes(model.model)) {
223
224
  // We don't know what the value means for these devices.
224
225
  // https://github.com/Koenkk/zigbee2mqtt/issues/11126
225
226
  // https://github.com/Koenkk/zigbee2mqtt/issues/12279
@@ -268,7 +269,7 @@ const numericAttributes2Payload = (msg, meta, model, options, dataObject) => {
268
269
  mapping = 'right';
269
270
  }
270
271
  payload[`state_${mapping}`] = value === 1 ? 'ON' : 'OFF';
271
- } else if (['RTCGQ12LM', 'RTCGQ14LM'].includes(model.model)) {
272
+ } else if (['RTCGQ12LM', 'RTCGQ14LM', 'RTCGQ15LM'].includes(model.model)) {
272
273
  // Sometimes RTCGQ14LM reports high illuminance values in the dark
273
274
  // https://github.com/Koenkk/zigbee2mqtt/issues/12596
274
275
  const illuminance = value > 65000 ? 0 : value;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "14.0.590",
3
+ "version": "14.0.593",
4
4
  "description": "Collection of device converters to be used with zigbee-herdsman",
5
5
  "main": "index.js",
6
6
  "files": [