zigbee-herdsman-converters 14.0.401 → 14.0.405

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.
@@ -1046,7 +1046,7 @@ const converters = {
1046
1046
  cluster: 'genScenes',
1047
1047
  type: 'commandRecall',
1048
1048
  convert: (model, msg, publish, options, meta) => {
1049
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1049
+ if (hasAlreadyProcessedMessage(msg)) return;
1050
1050
  const payload = {action: postfixWithEndpointName(`recall_${msg.data.sceneid}`, msg, model)};
1051
1051
  addActionGroup(payload, msg, model);
1052
1052
  return payload;
@@ -1056,7 +1056,7 @@ const converters = {
1056
1056
  cluster: 'ssIasAce',
1057
1057
  type: 'commandPanic',
1058
1058
  convert: (model, msg, publish, options, meta) => {
1059
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1059
+ if (hasAlreadyProcessedMessage(msg)) return;
1060
1060
  const payload = {action: postfixWithEndpointName(`panic`, msg, model)};
1061
1061
  addActionGroup(payload, msg, model);
1062
1062
  return payload;
@@ -1066,7 +1066,7 @@ const converters = {
1066
1066
  cluster: 'ssIasAce',
1067
1067
  type: 'commandArm',
1068
1068
  convert: (model, msg, publish, options, meta) => {
1069
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1069
+ if (hasAlreadyProcessedMessage(msg)) return;
1070
1070
  const payload = {
1071
1071
  action: postfixWithEndpointName(constants.armMode[msg.data['armmode']], msg, model),
1072
1072
  action_code: msg.data.code,
@@ -1090,7 +1090,7 @@ const converters = {
1090
1090
  cluster: 'closuresWindowCovering',
1091
1091
  type: 'commandStop',
1092
1092
  convert: (model, msg, publish, options, meta) => {
1093
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1093
+ if (hasAlreadyProcessedMessage(msg)) return;
1094
1094
  const payload = {action: postfixWithEndpointName('stop', msg, model)};
1095
1095
  addActionGroup(payload, msg, model);
1096
1096
  return payload;
@@ -1100,7 +1100,7 @@ const converters = {
1100
1100
  cluster: 'closuresWindowCovering',
1101
1101
  type: 'commandUpOpen',
1102
1102
  convert: (model, msg, publish, options, meta) => {
1103
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1103
+ if (hasAlreadyProcessedMessage(msg)) return;
1104
1104
  const payload = {action: postfixWithEndpointName('open', msg, model)};
1105
1105
  addActionGroup(payload, msg, model);
1106
1106
  return payload;
@@ -1110,7 +1110,7 @@ const converters = {
1110
1110
  cluster: 'closuresWindowCovering',
1111
1111
  type: 'commandDownClose',
1112
1112
  convert: (model, msg, publish, options, meta) => {
1113
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1113
+ if (hasAlreadyProcessedMessage(msg)) return;
1114
1114
  const payload = {action: postfixWithEndpointName('close', msg, model)};
1115
1115
  addActionGroup(payload, msg, model);
1116
1116
  return payload;
@@ -1120,7 +1120,7 @@ const converters = {
1120
1120
  cluster: 'genOnOff',
1121
1121
  type: 'commandOn',
1122
1122
  convert: (model, msg, publish, options, meta) => {
1123
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1123
+ if (hasAlreadyProcessedMessage(msg)) return;
1124
1124
  const payload = {action: postfixWithEndpointName('on', msg, model)};
1125
1125
  addActionGroup(payload, msg, model);
1126
1126
  return payload;
@@ -1130,7 +1130,7 @@ const converters = {
1130
1130
  cluster: 'genOnOff',
1131
1131
  type: 'commandOff',
1132
1132
  convert: (model, msg, publish, options, meta) => {
1133
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1133
+ if (hasAlreadyProcessedMessage(msg)) return;
1134
1134
  const payload = {action: postfixWithEndpointName('off', msg, model)};
1135
1135
  addActionGroup(payload, msg, model);
1136
1136
  return payload;
@@ -1140,7 +1140,7 @@ const converters = {
1140
1140
  cluster: 'genOnOff',
1141
1141
  type: 'commandOffWithEffect',
1142
1142
  convert: (model, msg, publish, options, meta) => {
1143
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1143
+ if (hasAlreadyProcessedMessage(msg)) return;
1144
1144
  const payload = {action: postfixWithEndpointName(`off`, msg, model)};
1145
1145
  addActionGroup(payload, msg, model);
1146
1146
  return payload;
@@ -1150,7 +1150,7 @@ const converters = {
1150
1150
  cluster: 'genOnOff',
1151
1151
  type: 'commandToggle',
1152
1152
  convert: (model, msg, publish, options, meta) => {
1153
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1153
+ if (hasAlreadyProcessedMessage(msg)) return;
1154
1154
  const payload = {action: postfixWithEndpointName('toggle', msg, model)};
1155
1155
  addActionGroup(payload, msg, model);
1156
1156
  return payload;
@@ -1161,7 +1161,7 @@ const converters = {
1161
1161
  type: ['commandMoveToLevel', 'commandMoveToLevelWithOnOff'],
1162
1162
  options: [exposes.options.simulated_brightness()],
1163
1163
  convert: (model, msg, publish, options, meta) => {
1164
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1164
+ if (hasAlreadyProcessedMessage(msg)) return;
1165
1165
  const payload = {
1166
1166
  action: postfixWithEndpointName(`brightness_move_to_level`, msg, model),
1167
1167
  action_level: msg.data.level,
@@ -1183,7 +1183,7 @@ const converters = {
1183
1183
  type: ['commandMove', 'commandMoveWithOnOff'],
1184
1184
  options: [exposes.options.simulated_brightness()],
1185
1185
  convert: (model, msg, publish, options, meta) => {
1186
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1186
+ if (hasAlreadyProcessedMessage(msg)) return;
1187
1187
  const direction = msg.data.movemode === 1 ? 'down' : 'up';
1188
1188
  const action = postfixWithEndpointName(`brightness_move_${direction}`, msg, model);
1189
1189
  const payload = {action, action_rate: msg.data.rate};
@@ -1219,7 +1219,7 @@ const converters = {
1219
1219
  type: ['commandStep', 'commandStepWithOnOff'],
1220
1220
  options: [exposes.options.simulated_brightness()],
1221
1221
  convert: (model, msg, publish, options, meta) => {
1222
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1222
+ if (hasAlreadyProcessedMessage(msg)) return;
1223
1223
  const direction = msg.data.stepmode === 1 ? 'down' : 'up';
1224
1224
  const payload = {
1225
1225
  action: postfixWithEndpointName(`brightness_step_${direction}`, msg, model),
@@ -1246,7 +1246,7 @@ const converters = {
1246
1246
  type: ['commandStop', 'commandStopWithOnOff'],
1247
1247
  options: [exposes.options.simulated_brightness()],
1248
1248
  convert: (model, msg, publish, options, meta) => {
1249
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1249
+ if (hasAlreadyProcessedMessage(msg)) return;
1250
1250
  if (options.simulated_brightness) {
1251
1251
  clearInterval(globalStore.getValue(msg.endpoint, 'simulated_brightness_timer'));
1252
1252
  globalStore.putValue(msg.endpoint, 'simulated_brightness_timer', undefined);
@@ -1261,7 +1261,7 @@ const converters = {
1261
1261
  cluster: 'lightingColorCtrl',
1262
1262
  type: ['commandMoveColorTemp'],
1263
1263
  convert: (model, msg, publish, options, meta) => {
1264
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1264
+ if (hasAlreadyProcessedMessage(msg)) return;
1265
1265
  const direction = msg.data.movemode === 1 ? 'down' : 'up';
1266
1266
  const action = postfixWithEndpointName(`color_temperature_move_${direction}`, msg, model);
1267
1267
  const payload = {action, action_rate: msg.data.rate, action_minimum: msg.data.minimum, action_maximum: msg.data.maximum};
@@ -1273,7 +1273,7 @@ const converters = {
1273
1273
  cluster: 'lightingColorCtrl',
1274
1274
  type: 'commandStepColorTemp',
1275
1275
  convert: (model, msg, publish, options, meta) => {
1276
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1276
+ if (hasAlreadyProcessedMessage(msg)) return;
1277
1277
  const direction = msg.data.stepmode === 1 ? 'up' : 'down';
1278
1278
  const payload = {
1279
1279
  action: postfixWithEndpointName(`color_temperature_step_${direction}`, msg, model),
@@ -1292,7 +1292,7 @@ const converters = {
1292
1292
  cluster: 'lightingColorCtrl',
1293
1293
  type: 'commandEnhancedMoveToHueAndSaturation',
1294
1294
  convert: (model, msg, publish, options, meta) => {
1295
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1295
+ if (hasAlreadyProcessedMessage(msg)) return;
1296
1296
  const payload = {
1297
1297
  action: postfixWithEndpointName(`enhanced_move_to_hue_and_saturation`, msg, model),
1298
1298
  action_enhanced_hue: msg.data.enhancehue,
@@ -1309,7 +1309,7 @@ const converters = {
1309
1309
  cluster: 'lightingColorCtrl',
1310
1310
  type: ['commandStepHue'],
1311
1311
  convert: (model, msg, publish, options, meta) => {
1312
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1312
+ if (hasAlreadyProcessedMessage(msg)) return;
1313
1313
  const direction = msg.data.stepmode === 1 ? 'up' : 'down';
1314
1314
  const payload = {
1315
1315
  action: postfixWithEndpointName(`color_hue_step_${direction}`, msg, model),
@@ -1324,7 +1324,7 @@ const converters = {
1324
1324
  cluster: 'lightingColorCtrl',
1325
1325
  type: ['commandStepSaturation'],
1326
1326
  convert: (model, msg, publish, options, meta) => {
1327
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1327
+ if (hasAlreadyProcessedMessage(msg)) return;
1328
1328
  const direction = msg.data.stepmode === 1 ? 'up' : 'down';
1329
1329
  const payload = {
1330
1330
  action: postfixWithEndpointName(`color_saturation_step_${direction}`, msg, model),
@@ -1339,7 +1339,7 @@ const converters = {
1339
1339
  cluster: 'lightingColorCtrl',
1340
1340
  type: 'commandColorLoopSet',
1341
1341
  convert: (model, msg, publish, options, meta) => {
1342
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1342
+ if (hasAlreadyProcessedMessage(msg)) return;
1343
1343
  const updateFlags = msg.data.updateflags;
1344
1344
  const actionLookup = {
1345
1345
  0x00: 'deactivate',
@@ -1369,7 +1369,7 @@ const converters = {
1369
1369
  cluster: 'lightingColorCtrl',
1370
1370
  type: 'commandMoveToColorTemp',
1371
1371
  convert: (model, msg, publish, options, meta) => {
1372
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1372
+ if (hasAlreadyProcessedMessage(msg)) return;
1373
1373
  const payload = {
1374
1374
  action: postfixWithEndpointName(`color_temperature_move`, msg, model),
1375
1375
  action_color_temperature: msg.data.colortemp,
@@ -1383,7 +1383,7 @@ const converters = {
1383
1383
  cluster: 'lightingColorCtrl',
1384
1384
  type: 'commandMoveToColor',
1385
1385
  convert: (model, msg, publish, options, meta) => {
1386
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1386
+ if (hasAlreadyProcessedMessage(msg)) return;
1387
1387
  const payload = {
1388
1388
  action: postfixWithEndpointName(`color_move`, msg, model),
1389
1389
  action_color: {
@@ -1400,7 +1400,7 @@ const converters = {
1400
1400
  cluster: 'lightingColorCtrl',
1401
1401
  type: 'commandMoveHue',
1402
1402
  convert: (model, msg, publish, options, meta) => {
1403
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1403
+ if (hasAlreadyProcessedMessage(msg)) return;
1404
1404
  const movestop = msg.data.movemode == 1 ? 'move' : 'stop';
1405
1405
  const action = postfixWithEndpointName(`hue_${movestop}`, msg, model);
1406
1406
  const payload = {action, action_rate: msg.data.rate};
@@ -1412,7 +1412,7 @@ const converters = {
1412
1412
  cluster: 'lightingColorCtrl',
1413
1413
  type: 'commandMoveToSaturation',
1414
1414
  convert: (model, msg, publish, options, meta) => {
1415
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1415
+ if (hasAlreadyProcessedMessage(msg)) return;
1416
1416
  const payload = {
1417
1417
  action: postfixWithEndpointName('move_to_saturation', msg, model),
1418
1418
  action_saturation: msg.data.saturation,
@@ -1426,7 +1426,7 @@ const converters = {
1426
1426
  cluster: 'lightingColorCtrl',
1427
1427
  type: 'commandMoveToHue',
1428
1428
  convert: (model, msg, publish, options, meta) => {
1429
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1429
+ if (hasAlreadyProcessedMessage(msg)) return;
1430
1430
  const payload = {
1431
1431
  action: postfixWithEndpointName(`move_to_hue`, msg, model),
1432
1432
  action_hue: msg.data.hue,
@@ -1441,7 +1441,7 @@ const converters = {
1441
1441
  cluster: 'ssIasAce',
1442
1442
  type: 'commandEmergency',
1443
1443
  convert: (model, msg, publish, options, meta) => {
1444
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1444
+ if (hasAlreadyProcessedMessage(msg)) return;
1445
1445
  const payload = {action: postfixWithEndpointName(`emergency`, msg, model)};
1446
1446
  addActionGroup(payload, msg, model);
1447
1447
  return payload;
@@ -1451,7 +1451,7 @@ const converters = {
1451
1451
  cluster: 'genOnOff',
1452
1452
  type: 'commandOn',
1453
1453
  convert: (model, msg, publish, options, meta) => {
1454
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1454
+ if (hasAlreadyProcessedMessage(msg)) return;
1455
1455
  const property = postfixWithEndpointName('state', msg, model);
1456
1456
  return {[property]: 'ON'};
1457
1457
  },
@@ -1460,7 +1460,7 @@ const converters = {
1460
1460
  cluster: 'genOnOff',
1461
1461
  type: 'commandOff',
1462
1462
  convert: (model, msg, publish, options, meta) => {
1463
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1463
+ if (hasAlreadyProcessedMessage(msg)) return;
1464
1464
  const property = postfixWithEndpointName('state', msg, model);
1465
1465
  return {[property]: 'OFF'};
1466
1466
  },
@@ -2185,7 +2185,7 @@ const converters = {
2185
2185
  cluster: 'ssIasZone',
2186
2186
  type: 'commandStatusChangeNotification',
2187
2187
  convert: (model, msg, publish, options, meta) => {
2188
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
2188
+ if (hasAlreadyProcessedMessage(msg)) return;
2189
2189
  const lookup = {1: 'pressed'};
2190
2190
  const zoneStatus = msg.data.zonestatus;
2191
2191
  return {
@@ -3815,19 +3815,21 @@ const converters = {
3815
3815
  return {open_window_temperature: (value / 10).toFixed(1)};
3816
3816
  case tuya.dataPoints.tvErrorStatus:
3817
3817
  return {fault_alarm: value};
3818
- case tuya.dataPoints.tvHolidayMode:
3819
- return {holiday_mode_date: value};
3820
-
3818
+ case tuya.dataPoints.tvHolidayMode: {
3819
+ const sy = value.slice(0, 4); const sm = value.slice(4, 6); const sd = value.slice(6, 8);
3820
+ const sh = value.slice(8, 10); const smi = value.slice(10, 12); const ey = value.slice(12, 16);
3821
+ const em = value.slice(16, 18); const ed = value.slice(18, 20); const eh = value.slice(20, 22);
3822
+ const emi = value.slice(22, 24);
3823
+ const hMode = 'start --> ' + sy + ' - ' + sm + ' - ' + sd + ' ' + sh + ' : ' + smi +
3824
+ ' stop --> ' + ey + ' - ' + em + ' - ' + ed + ' ' + eh + ' : ' + emi;
3825
+ return {holiday_start_stop: hMode};
3826
+ }
3821
3827
  case tuya.dataPoints.tvBoostMode:
3822
- // Online ?
3828
+ // 115 online / Is the device online
3823
3829
  return {online: value ? 'ON' : 'OFF'};
3824
3830
  case tuya.dataPoints.tvWorkingDay:
3825
- // tvWorkingDay: 31,
3831
+ // DP-31, Send and Report, ENUM, Week select 0 - 5 days, 1 - 6 days, 2 - 7 days
3826
3832
  return {working_day: value};
3827
- case tuya.dataPoints.tvWeekSchedule:
3828
- // tvWeekSchedule: 106, Week select 0 - 5 days, 1 - 6 days, 2 - 7 days
3829
- return {week_schedule: value};
3830
-
3831
3833
  case tuya.dataPoints.tvMondaySchedule:
3832
3834
  return {schedule_monday:
3833
3835
  ' ' + value[0] / 6 + 'h:' + value[1] + 'm ' + value[2] / 10 + '°C' +
@@ -4522,7 +4524,7 @@ const converters = {
4522
4524
  cluster: 'genScenes',
4523
4525
  type: 'commandTradfriArrowSingle',
4524
4526
  convert: (model, msg, publish, options, meta) => {
4525
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
4527
+ if (hasAlreadyProcessedMessage(msg)) return;
4526
4528
  if (msg.data.value === 2) {
4527
4529
  // This is send on toggle hold, ignore it as a toggle_hold is already handled above.
4528
4530
  return;
@@ -4536,7 +4538,7 @@ const converters = {
4536
4538
  cluster: 'genScenes',
4537
4539
  type: 'commandTradfriArrowHold',
4538
4540
  convert: (model, msg, publish, options, meta) => {
4539
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
4541
+ if (hasAlreadyProcessedMessage(msg)) return;
4540
4542
  const direction = msg.data.value === 3329 ? 'left' : 'right';
4541
4543
  globalStore.putValue(msg.endpoint, 'direction', direction);
4542
4544
  return {action: `arrow_${direction}_hold`};
@@ -4547,7 +4549,7 @@ const converters = {
4547
4549
  type: 'commandTradfriArrowRelease',
4548
4550
  options: [exposes.options.legacy()],
4549
4551
  convert: (model, msg, publish, options, meta) => {
4550
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
4552
+ if (hasAlreadyProcessedMessage(msg)) return;
4551
4553
  const direction = globalStore.getValue(msg.endpoint, 'direction');
4552
4554
  if (direction) {
4553
4555
  globalStore.clearValue(msg.endpoint, 'direction');
@@ -5027,6 +5029,9 @@ const converters = {
5027
5029
  // dimmer
5028
5030
  else if (option0 === 0x0101) payload.device_mode = 'dimmer_on';
5029
5031
  else if (option0 === 0x0100) payload.device_mode = 'dimmer_off';
5032
+ // pilot wire
5033
+ else if (option0 === 0x0002) payload.device_mode = 'pilot_on';
5034
+ else if (option0 === 0x0001) payload.device_mode = 'pilot_off';
5030
5035
  // unknown case
5031
5036
  else {
5032
5037
  meta.logger.warn(`device_mode ${option0} not recognized, please fix me`);
@@ -5035,6 +5040,26 @@ const converters = {
5035
5040
  return payload;
5036
5041
  },
5037
5042
  },
5043
+ legrand_cable_outlet_mode: {
5044
+ cluster: '64576',
5045
+ type: ['readResponse'],
5046
+ convert: (model, msg, publish, options, meta) => {
5047
+ const payload = {};
5048
+ const mode = msg.data['0'];
5049
+
5050
+ if (mode === 0x00) payload.cable_outlet_mode = 'comfort';
5051
+ else if (mode === 0x01) payload.cable_outlet_mode = 'comfort-1';
5052
+ else if (mode === 0x02) payload.cable_outlet_mode = 'comfort-2';
5053
+ else if (mode === 0x03) payload.cable_outlet_mode = 'eco';
5054
+ else if (mode === 0x04) payload.cable_outlet_mode = 'frost_protection';
5055
+ else if (mode === 0x05) payload.cable_outlet_mode = 'off';
5056
+ else {
5057
+ meta.logger.warn(`Bad mode : ${mode}`);
5058
+ payload.cable_outlet_mode = 'unknown';
5059
+ }
5060
+ return payload;
5061
+ },
5062
+ },
5038
5063
  legrand_power_alarm: {
5039
5064
  cluster: 'haElectricalMeasurement',
5040
5065
  type: ['attributeReport', 'readResponse'],
@@ -5255,31 +5280,45 @@ const converters = {
5255
5280
  payload.battery = batteryVoltageToPercentage(value, '3V_2100');
5256
5281
  } else if (index === 3) payload.temperature = calibrateAndPrecisionRoundOptions(value, options, 'temperature'); // 0x03
5257
5282
  else if (index === 5) {
5258
- if (['JT-BZ-01AQ/A'].includes(model.model)) payload.power_outage_count = value;
5283
+ if (['JT-BZ-01AQ/A', 'RTCZCGQ11LM'].includes(model.model)) payload.power_outage_count = value;
5259
5284
  } else if (index === 100) {
5260
- if (['QBKG20LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM'].includes(model.model)) {
5285
+ if (['QBKG20LM', 'QBKG31LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM'].includes(model.model)) {
5261
5286
  const mapping = model.model === 'QBCZ15LM' ? 'relay' : 'left';
5262
5287
  payload[`state_${mapping}`] = value === 1 ? 'ON' : 'OFF';
5263
5288
  } else if (['WXKG14LM', 'WXKG16LM', 'WXKG17LM'].includes(model.model)) {
5264
5289
  payload.click_mode = {1: 'fast', 2: 'multi'}[value];
5290
+ } else if (['WXCJKG11LM ', 'WXCJKG12LM', 'WXCJKG13LM'].includes(model.model)) {
5291
+ // We don't know what the value means for these devices.
5292
+ // https://github.com/Koenkk/zigbee2mqtt/issues/11126
5265
5293
  } else {
5266
5294
  payload.state = value === 1 ? 'ON' : 'OFF';
5267
5295
  }
5268
5296
  } else if (index === 101) {
5269
- if (['QBKG19LM', 'QBKG20LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM'].includes(model.model)) {
5297
+ if (['QBKG20LM', 'QBKG31LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM'].includes(model.model)) {
5270
5298
  const mapping = model.model === 'QBCZ15LM' ? 'usb' : 'right';
5271
5299
  payload[`state_${mapping}`] = value === 1 ? 'ON' : 'OFF';
5272
5300
  } else if (['QBKG25LM', 'QBKG34LM'].includes(model.model)) {
5273
5301
  payload.state_center = value === 1 ? 'ON' : 'OFF';
5274
5302
  } else if (['RTCGQ12LM'].includes(model.model)) {
5275
5303
  payload.illuminance = calibrateAndPrecisionRoundOptions(value, options, 'illuminance');
5304
+ } else if (['ZNJLBL01LM'].includes(model.model)) {
5305
+ payload.battery = value;
5276
5306
  }
5277
5307
  } else if (index ===102 ) {
5278
5308
  if (['QBKG25LM', 'QBKG34LM'].includes(model.model)) {
5279
5309
  payload.state_right = value === 1 ? 'ON' : 'OFF';
5310
+ } else if (['RTCZCGQ11LM'].includes(model.model)) {
5311
+ payload.presence_event = {0: 'enter', 1: 'leave', 2: 'left_enter', 3: 'right_leave', 4: 'right_enter',
5312
+ 5: 'left_leave', 6: 'approach', 7: 'away', 255: null}[value];
5313
+ }
5314
+ } else if (index ===103) payload.monitoring_mode = value === 1 ? 'left_right' : 'undirected'; // RTCZCGQ11LM
5315
+ else if (index === 105) {
5316
+ if (['RTCGQ13LM'].includes(model.model)) {
5317
+ payload.motion_sensitivity = {1: 'low', 2: 'medium', 3: 'high'}[value];
5318
+ } else if (['RTCZCGQ11LM'].includes(model.model)) {
5319
+ payload.approach_distance = {0: 'far', 1: 'medium', 2: 'near'}[value];
5280
5320
  }
5281
- } else if (index === 105) payload.motion_sensitivity = {1: 'low', 2: 'medium', 3: 'high'}[value]; // RTCGQ13LM
5282
- else if (index === 149) {
5321
+ } else if (index === 149) {
5283
5322
  payload.energy = precisionRound(value, 2); // 0x95
5284
5323
  // Consumption is deprecated
5285
5324
  payload.consumption = payload.energy;
@@ -5303,6 +5342,7 @@ const converters = {
5303
5342
  }
5304
5343
  if (msg.data.hasOwnProperty('4')) payload.mode_switch = {4: 'anti_flicker_mode', 1: 'quick_mode'}[msg.data['4']];
5305
5344
  if (msg.data.hasOwnProperty('10')) payload.switch_type = {1: 'toggle', 2: 'momentary'}[msg.data['10']];
5345
+ if (msg.data.hasOwnProperty('240')) payload.flip_indicator_light = msg.data['240'] === 1 ? 'ON' : 'OFF';
5306
5346
  if (msg.data.hasOwnProperty('258')) payload.detection_interval = msg.data['258'];
5307
5347
  if (msg.data.hasOwnProperty('268')) {
5308
5348
  if (['RTCGQ13LM'].includes(model.model)) {
@@ -5317,6 +5357,15 @@ const converters = {
5317
5357
  if (msg.data.hasOwnProperty('313')) payload.state = msg.data['313'] === 1 ? 'preparation' : 'work'; // JT-BZ-01AQ/A
5318
5358
  if (msg.data.hasOwnProperty('314')) payload.gas = msg.data['314'] === 1; // JT-BZ-01AQ/A
5319
5359
  if (msg.data.hasOwnProperty('315')) payload.gas_density = msg.data['315']; // JT-BZ-01AQ/A
5360
+ if (msg.data.hasOwnProperty('322')) payload.presence = msg.data['322'] === 1; // RTCZCGQ11LM
5361
+ if (msg.data.hasOwnProperty('323')) {
5362
+ payload.presence_event = {0: 'enter', 1: 'leave', 2: 'left_enter', 3: 'right_leave', 4: 'right_enter',
5363
+ 5: 'left_leave', 6: 'approach', 7: 'away'}[msg.data['323']]; // RTCZCGQ11LM
5364
+ }
5365
+ // RTCZCGQ11LM
5366
+ if (msg.data.hasOwnProperty('324')) payload.monitoring_mode = msg.data['324'] === 1 ? 'left_right' : 'undirected';
5367
+ // RTCZCGQ11LM
5368
+ if (msg.data.hasOwnProperty('326')) payload.approach_distance = {0: 'far', 1: 'medium', 2: 'near'}[msg.data['326']];
5320
5369
  if (msg.data.hasOwnProperty('331')) payload.linkage_alarm = msg.data['331'] === 1; // JT-BZ-01AQ/A
5321
5370
  if (msg.data.hasOwnProperty('512')) {
5322
5371
  if (['ZNCZ15LM', 'QBCZ14LM', 'QBCZ15LM'].includes(model.model)) {
@@ -5333,6 +5382,7 @@ const converters = {
5333
5382
  if (msg.data.hasOwnProperty('523')) payload.overload_protection = precisionRound(msg.data['523'], 2);
5334
5383
  if (msg.data.hasOwnProperty('550')) payload.button_switch_mode = msg.data['550'] === 1 ? 'relay_and_usb' : 'relay';
5335
5384
  if (msg.data['mode'] !== undefined) payload.operation_mode = ['command', 'event'][msg.data['mode']];
5385
+ if (msg.data.hasOwnProperty('1289')) payload.dimmer_mode = {3: 'rgbw', 1: 'dual_ct'}[msg.data['1289']];
5336
5386
  return payload;
5337
5387
  },
5338
5388
  },
@@ -5395,14 +5445,15 @@ const converters = {
5395
5445
  cluster: 'genMultistateInput',
5396
5446
  type: ['attributeReport'],
5397
5447
  convert: (model, msg, publish, options, meta) => {
5398
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5448
+ if (hasAlreadyProcessedMessage(msg)) return;
5399
5449
  let actionLookup = {0: 'hold', 1: 'single', 2: 'double', 3: 'triple', 255: 'release'};
5400
5450
  if (model.model === 'WXKG12LM') {
5401
5451
  actionLookup = {...actionLookup, 16: 'hold', 17: 'release', 18: 'shake'};
5402
5452
  }
5403
5453
 
5404
5454
  let buttonLookup = null;
5405
- if (['WXKG02LM_rev2', 'WXKG07LM', 'WXKG17LM'].includes(model.model)) buttonLookup = {1: 'left', 2: 'right', 3: 'both'};
5455
+ if (['WXKG02LM_rev2', 'WXKG07LM', 'WXKG15LM', 'WXKG17LM',
5456
+ 'WRS-R02'].includes(model.model)) buttonLookup = {1: 'left', 2: 'right', 3: 'both'};
5406
5457
  if (['QBKG12LM', 'QBKG24LM'].includes(model.model)) buttonLookup = {5: 'left', 6: 'right', 7: 'both'};
5407
5458
  if (['QBKG39LM', 'QBKG41LM', 'WS-EUK02', 'WS-EUK04', 'QBKG20LM', 'QBKG31LM'].includes(model.model)) {
5408
5459
  buttonLookup = {41: 'left', 42: 'right', 51: 'both'};
@@ -5541,7 +5592,7 @@ const converters = {
5541
5592
  `increase it with this option (value is in ms).`),
5542
5593
  ],
5543
5594
  convert: (model, msg, publish, options, meta) => {
5544
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5595
+ if (hasAlreadyProcessedMessage(msg)) return;
5545
5596
  const state = msg.data['onOff'];
5546
5597
 
5547
5598
  // 0 = click down, 1 = click up, else = multiple clicks
@@ -5805,6 +5856,40 @@ const converters = {
5805
5856
  }
5806
5857
  },
5807
5858
  },
5859
+ xiaomi_curtain_acn002_position: {
5860
+ cluster: 'genAnalogOutput',
5861
+ type: ['attributeReport', 'readResponse'],
5862
+ options: [exposes.options.invert_cover()],
5863
+ convert: (model, msg, publish, options, meta) => {
5864
+ let position = precisionRound(msg.data['presentValue'], 2);
5865
+ position = options.invert_cover ? 100 - position : position;
5866
+ return {position: position};
5867
+ },
5868
+ },
5869
+ xiaomi_curtain_acn002_status: {
5870
+ cluster: 'genMultistateOutput',
5871
+ type: ['attributeReport', 'readResponse'],
5872
+ convert: (model, msg, publish, options, meta) => {
5873
+ let running = false;
5874
+ const data = msg.data;
5875
+ const lookup = {
5876
+ 0: 'declining',
5877
+ 1: 'rising',
5878
+ 2: 'pause',
5879
+ 3: 'blocked',
5880
+ };
5881
+ if (data && data.hasOwnProperty('presentValue')) {
5882
+ const value = data['presentValue'];
5883
+ if (value < 2) {
5884
+ running = true;
5885
+ }
5886
+ return {
5887
+ motor_state: lookup[value],
5888
+ running: running,
5889
+ };
5890
+ }
5891
+ },
5892
+ },
5808
5893
  xiaomi_operation_mode_basic: {
5809
5894
  cluster: 'genBasic',
5810
5895
  type: ['attributeReport', 'readResponse'],
@@ -5847,7 +5932,7 @@ const converters = {
5847
5932
  cluster: 'genMultistateInput',
5848
5933
  type: ['attributeReport', 'readResponse'],
5849
5934
  convert: (model, msg, publish, options, meta) => {
5850
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5935
+ if (hasAlreadyProcessedMessage(msg)) return;
5851
5936
  const actionLookup = {0: 'hold', 255: 'release', 1: 'single', 2: 'double', 3: 'triple', 5: 'quintuple', 6: 'many'};
5852
5937
  const button = msg.endpoint.ID;
5853
5938
  const value = msg.data.presentValue;
@@ -5870,7 +5955,7 @@ const converters = {
5870
5955
  cluster: 'genOnOff',
5871
5956
  type: 'commandOn',
5872
5957
  convert: (model, msg, publish, options, meta) => {
5873
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5958
+ if (hasAlreadyProcessedMessage(msg)) return;
5874
5959
  return {action: 'button_2_single'};
5875
5960
  },
5876
5961
  },
@@ -5878,7 +5963,7 @@ const converters = {
5878
5963
  cluster: 'genOnOff',
5879
5964
  type: 'commandOff',
5880
5965
  convert: (model, msg, publish, options, meta) => {
5881
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5966
+ if (hasAlreadyProcessedMessage(msg)) return;
5882
5967
  return {action: 'button_1_single'};
5883
5968
  },
5884
5969
  },
@@ -5886,7 +5971,7 @@ const converters = {
5886
5971
  cluster: 'genLevelCtrl',
5887
5972
  type: 'commandStep',
5888
5973
  convert: (model, msg, publish, options, meta) => {
5889
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5974
+ if (hasAlreadyProcessedMessage(msg)) return;
5890
5975
  const button = msg.data.stepmode === 0 ? '4' : '3';
5891
5976
  return {action: `button_${button}_single`};
5892
5977
  },
@@ -5896,7 +5981,7 @@ const converters = {
5896
5981
  type: 'commandStop',
5897
5982
  options: [exposes.options.legacy()],
5898
5983
  convert: (model, msg, publish, options, meta) => {
5899
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5984
+ if (hasAlreadyProcessedMessage(msg)) return;
5900
5985
  if (globalStore.hasValue(msg.endpoint, 'button')) {
5901
5986
  const value = globalStore.getValue(msg.endpoint, 'button');
5902
5987
  const duration = Date.now() - value.start;
@@ -5910,7 +5995,7 @@ const converters = {
5910
5995
  cluster: 'genLevelCtrl',
5911
5996
  type: 'commandMove',
5912
5997
  convert: (model, msg, publish, options, meta) => {
5913
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5998
+ if (hasAlreadyProcessedMessage(msg)) return;
5914
5999
  const button = msg.data.movemode === 0 ? '4' : '3';
5915
6000
  globalStore.putValue(msg.endpoint, 'button', {button, start: Date.now()});
5916
6001
  return {action: `button_${button}_hold`};
@@ -5920,7 +6005,7 @@ const converters = {
5920
6005
  cluster: 'lightingColorCtrl',
5921
6006
  type: 'commandStepColorTemp',
5922
6007
  convert: (model, msg, publish, options, meta) => {
5923
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
6008
+ if (hasAlreadyProcessedMessage(msg)) return;
5924
6009
  let action;
5925
6010
  if (model.model === 'WXCJKG12LM') {
5926
6011
  // for WXCJKG12LM model it's double click event on buttons 3 and 4
@@ -5937,7 +6022,7 @@ const converters = {
5937
6022
  type: 'commandMoveColorTemp',
5938
6023
  options: [exposes.options.legacy()],
5939
6024
  convert: (model, msg, publish, options, meta) => {
5940
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
6025
+ if (hasAlreadyProcessedMessage(msg)) return;
5941
6026
  const stop = msg.data.movemode === 0;
5942
6027
  let result = null;
5943
6028
  if (stop) {
@@ -7363,7 +7448,7 @@ const converters = {
7363
7448
  cluster: 'wiserDeviceInfo',
7364
7449
  type: 'attributeReport',
7365
7450
  convert: (model, msg, publish, options, meta) => {
7366
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
7451
+ if (hasAlreadyProcessedMessage(msg)) return;
7367
7452
 
7368
7453
  const data = msg.data['deviceInfo'].split(',');
7369
7454
  if (data[0] === 'UI' && data[1]) {
@@ -8141,7 +8226,7 @@ const converters = {
8141
8226
  convert: (model, msg, publish, options, meta) => {
8142
8227
  // commandStopMove without params
8143
8228
  if (msg.data[2] !== 71) return;
8144
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
8229
+ if (hasAlreadyProcessedMessage(msg)) return;
8145
8230
  const movestop = 'stop';
8146
8231
  const action = postfixWithEndpointName(`hue_${movestop}`, msg, model);
8147
8232
  const payload = {action};