zigbee-herdsman-converters 14.0.402 → 14.0.403

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 {
@@ -4522,7 +4522,7 @@ const converters = {
4522
4522
  cluster: 'genScenes',
4523
4523
  type: 'commandTradfriArrowSingle',
4524
4524
  convert: (model, msg, publish, options, meta) => {
4525
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
4525
+ if (hasAlreadyProcessedMessage(msg)) return;
4526
4526
  if (msg.data.value === 2) {
4527
4527
  // This is send on toggle hold, ignore it as a toggle_hold is already handled above.
4528
4528
  return;
@@ -4536,7 +4536,7 @@ const converters = {
4536
4536
  cluster: 'genScenes',
4537
4537
  type: 'commandTradfriArrowHold',
4538
4538
  convert: (model, msg, publish, options, meta) => {
4539
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
4539
+ if (hasAlreadyProcessedMessage(msg)) return;
4540
4540
  const direction = msg.data.value === 3329 ? 'left' : 'right';
4541
4541
  globalStore.putValue(msg.endpoint, 'direction', direction);
4542
4542
  return {action: `arrow_${direction}_hold`};
@@ -4547,7 +4547,7 @@ const converters = {
4547
4547
  type: 'commandTradfriArrowRelease',
4548
4548
  options: [exposes.options.legacy()],
4549
4549
  convert: (model, msg, publish, options, meta) => {
4550
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
4550
+ if (hasAlreadyProcessedMessage(msg)) return;
4551
4551
  const direction = globalStore.getValue(msg.endpoint, 'direction');
4552
4552
  if (direction) {
4553
4553
  globalStore.clearValue(msg.endpoint, 'direction');
@@ -5257,7 +5257,7 @@ const converters = {
5257
5257
  else if (index === 5) {
5258
5258
  if (['JT-BZ-01AQ/A'].includes(model.model)) payload.power_outage_count = value;
5259
5259
  } else if (index === 100) {
5260
- if (['QBKG20LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM'].includes(model.model)) {
5260
+ if (['QBKG20LM', 'QBKG31LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM'].includes(model.model)) {
5261
5261
  const mapping = model.model === 'QBCZ15LM' ? 'relay' : 'left';
5262
5262
  payload[`state_${mapping}`] = value === 1 ? 'ON' : 'OFF';
5263
5263
  } else if (['WXKG14LM', 'WXKG16LM', 'WXKG17LM'].includes(model.model)) {
@@ -5266,7 +5266,7 @@ const converters = {
5266
5266
  payload.state = value === 1 ? 'ON' : 'OFF';
5267
5267
  }
5268
5268
  } else if (index === 101) {
5269
- if (['QBKG19LM', 'QBKG20LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM'].includes(model.model)) {
5269
+ if (['QBKG20LM', 'QBKG31LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM'].includes(model.model)) {
5270
5270
  const mapping = model.model === 'QBCZ15LM' ? 'usb' : 'right';
5271
5271
  payload[`state_${mapping}`] = value === 1 ? 'ON' : 'OFF';
5272
5272
  } else if (['QBKG25LM', 'QBKG34LM'].includes(model.model)) {
@@ -5303,6 +5303,7 @@ const converters = {
5303
5303
  }
5304
5304
  if (msg.data.hasOwnProperty('4')) payload.mode_switch = {4: 'anti_flicker_mode', 1: 'quick_mode'}[msg.data['4']];
5305
5305
  if (msg.data.hasOwnProperty('10')) payload.switch_type = {1: 'toggle', 2: 'momentary'}[msg.data['10']];
5306
+ if (msg.data.hasOwnProperty('240')) payload.flip_indicator_light = msg.data['240'] === 1 ? 'ON' : 'OFF';
5306
5307
  if (msg.data.hasOwnProperty('258')) payload.detection_interval = msg.data['258'];
5307
5308
  if (msg.data.hasOwnProperty('268')) {
5308
5309
  if (['RTCGQ13LM'].includes(model.model)) {
@@ -5395,7 +5396,7 @@ const converters = {
5395
5396
  cluster: 'genMultistateInput',
5396
5397
  type: ['attributeReport'],
5397
5398
  convert: (model, msg, publish, options, meta) => {
5398
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5399
+ if (hasAlreadyProcessedMessage(msg)) return;
5399
5400
  let actionLookup = {0: 'hold', 1: 'single', 2: 'double', 3: 'triple', 255: 'release'};
5400
5401
  if (model.model === 'WXKG12LM') {
5401
5402
  actionLookup = {...actionLookup, 16: 'hold', 17: 'release', 18: 'shake'};
@@ -5541,7 +5542,7 @@ const converters = {
5541
5542
  `increase it with this option (value is in ms).`),
5542
5543
  ],
5543
5544
  convert: (model, msg, publish, options, meta) => {
5544
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5545
+ if (hasAlreadyProcessedMessage(msg)) return;
5545
5546
  const state = msg.data['onOff'];
5546
5547
 
5547
5548
  // 0 = click down, 1 = click up, else = multiple clicks
@@ -5847,7 +5848,7 @@ const converters = {
5847
5848
  cluster: 'genMultistateInput',
5848
5849
  type: ['attributeReport', 'readResponse'],
5849
5850
  convert: (model, msg, publish, options, meta) => {
5850
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5851
+ if (hasAlreadyProcessedMessage(msg)) return;
5851
5852
  const actionLookup = {0: 'hold', 255: 'release', 1: 'single', 2: 'double', 3: 'triple', 5: 'quintuple', 6: 'many'};
5852
5853
  const button = msg.endpoint.ID;
5853
5854
  const value = msg.data.presentValue;
@@ -5870,7 +5871,7 @@ const converters = {
5870
5871
  cluster: 'genOnOff',
5871
5872
  type: 'commandOn',
5872
5873
  convert: (model, msg, publish, options, meta) => {
5873
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5874
+ if (hasAlreadyProcessedMessage(msg)) return;
5874
5875
  return {action: 'button_2_single'};
5875
5876
  },
5876
5877
  },
@@ -5878,7 +5879,7 @@ const converters = {
5878
5879
  cluster: 'genOnOff',
5879
5880
  type: 'commandOff',
5880
5881
  convert: (model, msg, publish, options, meta) => {
5881
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5882
+ if (hasAlreadyProcessedMessage(msg)) return;
5882
5883
  return {action: 'button_1_single'};
5883
5884
  },
5884
5885
  },
@@ -5886,7 +5887,7 @@ const converters = {
5886
5887
  cluster: 'genLevelCtrl',
5887
5888
  type: 'commandStep',
5888
5889
  convert: (model, msg, publish, options, meta) => {
5889
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5890
+ if (hasAlreadyProcessedMessage(msg)) return;
5890
5891
  const button = msg.data.stepmode === 0 ? '4' : '3';
5891
5892
  return {action: `button_${button}_single`};
5892
5893
  },
@@ -5896,7 +5897,7 @@ const converters = {
5896
5897
  type: 'commandStop',
5897
5898
  options: [exposes.options.legacy()],
5898
5899
  convert: (model, msg, publish, options, meta) => {
5899
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5900
+ if (hasAlreadyProcessedMessage(msg)) return;
5900
5901
  if (globalStore.hasValue(msg.endpoint, 'button')) {
5901
5902
  const value = globalStore.getValue(msg.endpoint, 'button');
5902
5903
  const duration = Date.now() - value.start;
@@ -5910,7 +5911,7 @@ const converters = {
5910
5911
  cluster: 'genLevelCtrl',
5911
5912
  type: 'commandMove',
5912
5913
  convert: (model, msg, publish, options, meta) => {
5913
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5914
+ if (hasAlreadyProcessedMessage(msg)) return;
5914
5915
  const button = msg.data.movemode === 0 ? '4' : '3';
5915
5916
  globalStore.putValue(msg.endpoint, 'button', {button, start: Date.now()});
5916
5917
  return {action: `button_${button}_hold`};
@@ -5920,7 +5921,7 @@ const converters = {
5920
5921
  cluster: 'lightingColorCtrl',
5921
5922
  type: 'commandStepColorTemp',
5922
5923
  convert: (model, msg, publish, options, meta) => {
5923
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5924
+ if (hasAlreadyProcessedMessage(msg)) return;
5924
5925
  let action;
5925
5926
  if (model.model === 'WXCJKG12LM') {
5926
5927
  // for WXCJKG12LM model it's double click event on buttons 3 and 4
@@ -5937,7 +5938,7 @@ const converters = {
5937
5938
  type: 'commandMoveColorTemp',
5938
5939
  options: [exposes.options.legacy()],
5939
5940
  convert: (model, msg, publish, options, meta) => {
5940
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5941
+ if (hasAlreadyProcessedMessage(msg)) return;
5941
5942
  const stop = msg.data.movemode === 0;
5942
5943
  let result = null;
5943
5944
  if (stop) {
@@ -7363,7 +7364,7 @@ const converters = {
7363
7364
  cluster: 'wiserDeviceInfo',
7364
7365
  type: 'attributeReport',
7365
7366
  convert: (model, msg, publish, options, meta) => {
7366
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
7367
+ if (hasAlreadyProcessedMessage(msg)) return;
7367
7368
 
7368
7369
  const data = msg.data['deviceInfo'].split(',');
7369
7370
  if (data[0] === 'UI' && data[1]) {
@@ -8141,7 +8142,7 @@ const converters = {
8141
8142
  convert: (model, msg, publish, options, meta) => {
8142
8143
  // commandStopMove without params
8143
8144
  if (msg.data[2] !== 71) return;
8144
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
8145
+ if (hasAlreadyProcessedMessage(msg)) return;
8145
8146
  const movestop = 'stop';
8146
8147
  const action = postfixWithEndpointName(`hue_${movestop}`, msg, model);
8147
8148
  const payload = {action};
@@ -2173,7 +2173,7 @@ const converters = {
2173
2173
  key: ['led_disabled_night'],
2174
2174
  convertSet: async (entity, key, value, meta) => {
2175
2175
  if (['ZNCZ04LM', 'ZNCZ15LM', 'QBCZ14LM', 'QBCZ15LM', 'QBKG19LM', 'QBKG20LM', 'QBKG25LM', 'QBKG26LM',
2176
- 'QBKG34LM', 'DLKZMK11LM'].includes(meta.mapped.model)) {
2176
+ 'QBKG31LM', 'QBKG34LM', 'DLKZMK11LM'].includes(meta.mapped.model)) {
2177
2177
  await entity.write('aqaraOpple', {0x0203: {value: value ? 1 : 0, type: 0x10}}, manufacturerOptions.xiaomi);
2178
2178
  } else if (['ZNCZ11LM'].includes(meta.mapped.model)) {
2179
2179
  const payload = value ?
@@ -2188,13 +2188,24 @@ const converters = {
2188
2188
  },
2189
2189
  convertGet: async (entity, key, meta) => {
2190
2190
  if (['ZNCZ04LM', 'ZNCZ15LM', 'QBCZ15LM', 'QBCZ14LM', 'QBKG19LM', 'QBKG20LM', 'QBKG25LM', 'QBKG26LM',
2191
- 'QBKG34LM', 'DLKZMK11LM'].includes(meta.mapped.model)) {
2191
+ 'QBKG31LM', 'QBKG34LM', 'DLKZMK11LM'].includes(meta.mapped.model)) {
2192
2192
  await entity.read('aqaraOpple', [0x0203], manufacturerOptions.xiaomi);
2193
2193
  } else {
2194
2194
  throw new Error('Not supported');
2195
2195
  }
2196
2196
  },
2197
2197
  },
2198
+ xiaomi_flip_indicator_light: {
2199
+ key: ['flip_indicator_light'],
2200
+ convertSet: async (entity, key, value, meta) => {
2201
+ const lookup = {'OFF': 0, 'ON': 1};
2202
+ await entity.write('aqaraOpple', {0x00F0: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi);
2203
+ return {state: {flip_indicator_light: value}};
2204
+ },
2205
+ convertGet: async (entity, key, meta) => {
2206
+ await entity.read('aqaraOpple', [0x00F0], manufacturerOptions.xiaomi);
2207
+ },
2208
+ },
2198
2209
  xiaomi_switch_operation_mode_basic: {
2199
2210
  key: ['operation_mode'],
2200
2211
  convertSet: async (entity, key, value, meta) => {
package/devices/casaia.js CHANGED
@@ -3,8 +3,16 @@ const fz = {...require('../converters/fromZigbee'), legacy: require('../lib/lega
3
3
  const reporting = require('../lib/reporting');
4
4
  const e = exposes.presets;
5
5
  const tz = require('../converters/toZigbee');
6
+ const extend = require('../lib/extend');
6
7
 
7
8
  module.exports = [
9
+ {
10
+ zigbeeModel: ['CSLC601-D-E'],
11
+ model: 'CSLC601-D-E',
12
+ vendor: 'CASAIA',
13
+ description: 'Dry contact relay switch module in 220v AC for gas boiler',
14
+ extend: extend.switch(),
15
+ },
8
16
  {
9
17
  zigbeeModel: ['CTHS317ET'],
10
18
  model: 'CTHS-317-ET',
@@ -134,11 +134,11 @@ module.exports = [
134
134
  model: '067774',
135
135
  vendor: 'Legrand',
136
136
  description: 'Wireless double remote switch',
137
- fromZigbee: [fz.identify, fz.command_on, fz.command_off, fz.command_toggle, fz.command_move, fz.command_stop, fz.battery],
137
+ fromZigbee: [fz.identify, fz.command_on, fzLocal.command_off, fz.command_toggle, fz.command_move, fz.command_stop, fz.battery],
138
138
  exposes: [e.battery(),
139
139
  e.action(['identify', 'on', 'off', 'toggle', 'brightness_move_up', 'brightness_move_down', 'brightness_stop'])],
140
140
  toZigbee: [],
141
- meta: {handleDuplicateTransaction: true, multiEndpoint: true, battery: {voltageToPercentage: '3V_2500'}},
141
+ meta: {multiEndpoint: true, battery: {voltageToPercentage: '3V_2500'}},
142
142
  configure: async (device, coordinatorEndpoint, logger) => {
143
143
  const endpoint = device.getEndpoint(1);
144
144
  await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'genOnOff', 'genLevelCtrl']);
package/devices/moes.js CHANGED
@@ -203,7 +203,7 @@ module.exports = [
203
203
  },
204
204
  },
205
205
  {
206
- fingerprint: [{modelID: 'TS0222', manufacturerName: '_TYZB01_kvwjujy9'}],
206
+ fingerprint: [{modelID: 'TS0222', manufacturerName: '_TYZB01_kvwjujy9'}, {modelID: 'TS0222', manufacturerName: '_TYZB01_ftdkanlj'}],
207
207
  model: 'ZSS-ZK-THL',
208
208
  vendor: 'Moes',
209
209
  description: 'Smart temperature and humidity meter with display',
package/devices/tuya.js CHANGED
@@ -12,7 +12,7 @@ const utils = require('../lib/utils');
12
12
 
13
13
  const TS011Fplugs = ['_TZ3000_5f43h46b', '_TZ3000_cphmq0q7', '_TZ3000_dpo1ysak', '_TZ3000_ew3ldmgx', '_TZ3000_gjnozsaz',
14
14
  '_TZ3000_jvzvulen', '_TZ3000_mraovvmm', '_TZ3000_nfnmi125', '_TZ3000_ps3dmato', '_TZ3000_w0qqde0g', '_TZ3000_u5u4cakc',
15
- '_TZ3000_rdtixbnu', '_TZ3000_typdpbpg', '_TZ3000_v1pdxuqq'];
15
+ '_TZ3000_rdtixbnu', '_TZ3000_typdpbpg'];
16
16
 
17
17
  const tzLocal = {
18
18
  TS0504B_color: {
@@ -168,6 +168,7 @@ module.exports = [
168
168
  {modelID: 'TS0505B', manufacturerName: '_TZ3000_1mtktxdk'},
169
169
  {modelID: 'TS0505B', manufacturerName: '_TZ3210_remypqqm'},
170
170
  {modelID: 'TS0505B', manufacturerName: '_TZ3000_kohbva1f'},
171
+ {modelID: 'TS0505B', manufacturerName: '_TZ3210_wslkvrau'},
171
172
  {modelID: 'TS0505B', manufacturerName: '_TZ3210_0rn9qhnu'},
172
173
  {modelID: 'TS0505B', manufacturerName: '_TZ3210_bicjqpg4'},
173
174
  {modelID: 'TS0505B', manufacturerName: '_TZ3000_cmaky9gq'},
@@ -221,6 +222,7 @@ module.exports = [
221
222
  fingerprint: [{modelID: 'TS0501B', manufacturerName: '_TZ3000_4whigl8i'},
222
223
  {modelID: 'TS0501B', manufacturerName: '_TZ3210_4whigl8i'},
223
224
  {modelID: 'TS0501B', manufacturerName: '_TZ3210_9q49basr'},
225
+ {modelID: 'TS0501B', manufacturerName: '_TZ3210_i680rtja'},
224
226
  {modelID: 'TS0501B', manufacturerName: '_TZ3210_grnwgegn'},
225
227
  {modelID: 'TS0501B', manufacturerName: '_TZ3210_wuheofsg'},
226
228
  {modelID: 'TS0501B', manufacturerName: '_TZ3210_e5t9bfdv'}],
@@ -1161,6 +1163,7 @@ module.exports = [
1161
1163
  {
1162
1164
  fingerprint: [
1163
1165
  {modelID: 'TS011F', manufacturerName: '_TZ3000_hyfvrar3'},
1166
+ {modelID: 'TS011F', manufacturerName: '_TZ3000_v1pdxuqq'},
1164
1167
  {modelID: 'TS011F', manufacturerName: '_TZ3000_bfn1w0mm'}],
1165
1168
  model: 'TS011F_plug_2',
1166
1169
  description: 'Smart plug (without power monitoring)',
package/devices/xiaomi.js CHANGED
@@ -312,22 +312,22 @@ module.exports = [
312
312
  model: 'QBKG31LM',
313
313
  vendor: 'Xiaomi',
314
314
  description: 'Aqara smart wall switch H1 Pro (with neutral, double rocker)',
315
- extend: extend.switch(),
316
- exposes: [e.power_outage_memory(), e.action(['single_left', 'double_left', 'single_right', 'double_right']),
317
- e.switch().withEndpoint('left'), e.switch().withEndpoint('right'), e.power().withAccess(ea.STATE_GET),
318
- exposes.enum('operation_mode', ea.ALL, ['control_relay', 'decoupled'])
319
- .withDescription('Decoupled mode for left button').withEndpoint('left'),
320
- exposes.enum('operation_mode', ea.ALL, ['control_relay', 'decoupled'])
321
- .withDescription('Decoupled mode for right button').withEndpoint('right')],
322
- fromZigbee: [fz.on_off, fz.xiaomi_power, fz.aqara_opple, fz.xiaomi_multistate_action],
323
- toZigbee: [tz.on_off, tz.xiaomi_power, tz.xiaomi_switch_operation_mode_opple, tz.xiaomi_switch_power_outage_memory],
324
315
  meta: {multiEndpoint: true},
325
316
  endpoint: (device) => {
326
317
  return {'left': 1, 'right': 2};
327
318
  },
328
- configure: async (device, coordinatorEndpoint, logger) => {
329
- await device.getEndpoint(1).write('aqaraOpple', {'mode': 1}, {manufacturerCode: 0x115f, disableResponse: true});
330
- },
319
+ fromZigbee: [fz.on_off, fz.xiaomi_power, fz.aqara_opple, fz.xiaomi_multistate_action],
320
+ toZigbee: [tz.on_off, tz.xiaomi_switch_operation_mode_opple, tz.xiaomi_switch_power_outage_memory,
321
+ tz.xiaomi_led_disabled_night, tz.xiaomi_flip_indicator_light],
322
+ exposes: [e.switch().withEndpoint('left'), e.switch().withEndpoint('right'),
323
+ e.power(), e.energy(), e.voltage(), e.temperature(), e.power_outage_memory(), e.led_disabled_night(), e.flip_indicator_light(),
324
+ e.action([
325
+ 'single_left', 'single_right', 'single_both',
326
+ 'double_left', 'double_right', 'double_both']),
327
+ exposes.enum('operation_mode', ea.ALL, ['control_relay', 'decoupled'])
328
+ .withDescription('Decoupled mode for left button').withEndpoint('left'),
329
+ exposes.enum('operation_mode', ea.ALL, ['control_relay', 'decoupled'])
330
+ .withDescription('Decoupled mode for right button').withEndpoint('right')],
331
331
  onEvent: preventReset,
332
332
  ota: ota.zigbeeOTA,
333
333
  },
package/lib/exposes.js CHANGED
@@ -537,6 +537,7 @@ module.exports = {
537
537
  effect: () => new Enum('effect', access.SET, ['blink', 'breathe', 'okay', 'channel_change', 'finish_effect', 'stop_effect']).withDescription('Triggers an effect on the light (e.g. make light blink for a few seconds)'),
538
538
  energy: () => new Numeric('energy', access.STATE).withUnit('kWh').withDescription('Sum of consumed energy'),
539
539
  fan: () => new Fan(),
540
+ flip_indicator_light: () => new Binary('flip_indicator_light', access.ALL, 'ON', 'OFF').withDescription('After turn on, the indicator light turns on while switch is off, and vice versa'),
540
541
  force: () => new Enum('force', access.STATE_SET, ['normal', 'open', 'close']).withDescription('Force the valve position'),
541
542
  formaldehyd: () => new Numeric('formaldehyd', access.STATE).withDescription('The measured formaldehyd value'),
542
543
  gas: () => new Binary('gas', access.STATE, true, false).withDescription('Indicates whether the device detected gas'),
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "14.0.402",
3
+ "version": "14.0.403",
4
4
  "lockfileVersion": 1,
5
5
  "requires": true,
6
6
  "dependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "14.0.402",
3
+ "version": "14.0.403",
4
4
  "description": "Collection of device converters to be used with zigbee-herdsman",
5
5
  "main": "index.js",
6
6
  "files": [