zigbee-herdsman-converters 14.0.399 → 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.
@@ -719,6 +719,7 @@ const converters = {
719
719
  {key: 'rmsVoltage', name: 'voltage', factor: 'acVoltage'},
720
720
  {key: 'rmsVoltagePhB', name: 'voltage_phase_b', factor: 'acVoltage'},
721
721
  {key: 'rmsVoltagePhC', name: 'voltage_phase_c', factor: 'acVoltage'},
722
+ {key: 'acFrequency', name: 'ac_frequency', factor: 'acFrequency'},
722
723
  ];
723
724
 
724
725
  const payload = {};
@@ -730,6 +731,9 @@ const converters = {
730
731
  payload[property] = calibrateAndPrecisionRoundOptions(value, options, entry.name);
731
732
  }
732
733
  }
734
+ if (msg.data.hasOwnProperty('powerFactor')) {
735
+ payload.power_factor = precisionRound(msg.data['powerFactor'] / 100, 2);
736
+ }
733
737
  return payload;
734
738
  },
735
739
  },
@@ -1042,7 +1046,7 @@ const converters = {
1042
1046
  cluster: 'genScenes',
1043
1047
  type: 'commandRecall',
1044
1048
  convert: (model, msg, publish, options, meta) => {
1045
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1049
+ if (hasAlreadyProcessedMessage(msg)) return;
1046
1050
  const payload = {action: postfixWithEndpointName(`recall_${msg.data.sceneid}`, msg, model)};
1047
1051
  addActionGroup(payload, msg, model);
1048
1052
  return payload;
@@ -1052,7 +1056,7 @@ const converters = {
1052
1056
  cluster: 'ssIasAce',
1053
1057
  type: 'commandPanic',
1054
1058
  convert: (model, msg, publish, options, meta) => {
1055
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1059
+ if (hasAlreadyProcessedMessage(msg)) return;
1056
1060
  const payload = {action: postfixWithEndpointName(`panic`, msg, model)};
1057
1061
  addActionGroup(payload, msg, model);
1058
1062
  return payload;
@@ -1062,7 +1066,7 @@ const converters = {
1062
1066
  cluster: 'ssIasAce',
1063
1067
  type: 'commandArm',
1064
1068
  convert: (model, msg, publish, options, meta) => {
1065
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1069
+ if (hasAlreadyProcessedMessage(msg)) return;
1066
1070
  const payload = {
1067
1071
  action: postfixWithEndpointName(constants.armMode[msg.data['armmode']], msg, model),
1068
1072
  action_code: msg.data.code,
@@ -1086,7 +1090,7 @@ const converters = {
1086
1090
  cluster: 'closuresWindowCovering',
1087
1091
  type: 'commandStop',
1088
1092
  convert: (model, msg, publish, options, meta) => {
1089
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1093
+ if (hasAlreadyProcessedMessage(msg)) return;
1090
1094
  const payload = {action: postfixWithEndpointName('stop', msg, model)};
1091
1095
  addActionGroup(payload, msg, model);
1092
1096
  return payload;
@@ -1096,7 +1100,7 @@ const converters = {
1096
1100
  cluster: 'closuresWindowCovering',
1097
1101
  type: 'commandUpOpen',
1098
1102
  convert: (model, msg, publish, options, meta) => {
1099
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1103
+ if (hasAlreadyProcessedMessage(msg)) return;
1100
1104
  const payload = {action: postfixWithEndpointName('open', msg, model)};
1101
1105
  addActionGroup(payload, msg, model);
1102
1106
  return payload;
@@ -1106,7 +1110,7 @@ const converters = {
1106
1110
  cluster: 'closuresWindowCovering',
1107
1111
  type: 'commandDownClose',
1108
1112
  convert: (model, msg, publish, options, meta) => {
1109
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1113
+ if (hasAlreadyProcessedMessage(msg)) return;
1110
1114
  const payload = {action: postfixWithEndpointName('close', msg, model)};
1111
1115
  addActionGroup(payload, msg, model);
1112
1116
  return payload;
@@ -1116,7 +1120,7 @@ const converters = {
1116
1120
  cluster: 'genOnOff',
1117
1121
  type: 'commandOn',
1118
1122
  convert: (model, msg, publish, options, meta) => {
1119
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1123
+ if (hasAlreadyProcessedMessage(msg)) return;
1120
1124
  const payload = {action: postfixWithEndpointName('on', msg, model)};
1121
1125
  addActionGroup(payload, msg, model);
1122
1126
  return payload;
@@ -1126,7 +1130,7 @@ const converters = {
1126
1130
  cluster: 'genOnOff',
1127
1131
  type: 'commandOff',
1128
1132
  convert: (model, msg, publish, options, meta) => {
1129
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1133
+ if (hasAlreadyProcessedMessage(msg)) return;
1130
1134
  const payload = {action: postfixWithEndpointName('off', msg, model)};
1131
1135
  addActionGroup(payload, msg, model);
1132
1136
  return payload;
@@ -1136,7 +1140,7 @@ const converters = {
1136
1140
  cluster: 'genOnOff',
1137
1141
  type: 'commandOffWithEffect',
1138
1142
  convert: (model, msg, publish, options, meta) => {
1139
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1143
+ if (hasAlreadyProcessedMessage(msg)) return;
1140
1144
  const payload = {action: postfixWithEndpointName(`off`, msg, model)};
1141
1145
  addActionGroup(payload, msg, model);
1142
1146
  return payload;
@@ -1146,7 +1150,7 @@ const converters = {
1146
1150
  cluster: 'genOnOff',
1147
1151
  type: 'commandToggle',
1148
1152
  convert: (model, msg, publish, options, meta) => {
1149
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1153
+ if (hasAlreadyProcessedMessage(msg)) return;
1150
1154
  const payload = {action: postfixWithEndpointName('toggle', msg, model)};
1151
1155
  addActionGroup(payload, msg, model);
1152
1156
  return payload;
@@ -1157,7 +1161,7 @@ const converters = {
1157
1161
  type: ['commandMoveToLevel', 'commandMoveToLevelWithOnOff'],
1158
1162
  options: [exposes.options.simulated_brightness()],
1159
1163
  convert: (model, msg, publish, options, meta) => {
1160
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1164
+ if (hasAlreadyProcessedMessage(msg)) return;
1161
1165
  const payload = {
1162
1166
  action: postfixWithEndpointName(`brightness_move_to_level`, msg, model),
1163
1167
  action_level: msg.data.level,
@@ -1179,7 +1183,7 @@ const converters = {
1179
1183
  type: ['commandMove', 'commandMoveWithOnOff'],
1180
1184
  options: [exposes.options.simulated_brightness()],
1181
1185
  convert: (model, msg, publish, options, meta) => {
1182
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1186
+ if (hasAlreadyProcessedMessage(msg)) return;
1183
1187
  const direction = msg.data.movemode === 1 ? 'down' : 'up';
1184
1188
  const action = postfixWithEndpointName(`brightness_move_${direction}`, msg, model);
1185
1189
  const payload = {action, action_rate: msg.data.rate};
@@ -1215,7 +1219,7 @@ const converters = {
1215
1219
  type: ['commandStep', 'commandStepWithOnOff'],
1216
1220
  options: [exposes.options.simulated_brightness()],
1217
1221
  convert: (model, msg, publish, options, meta) => {
1218
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1222
+ if (hasAlreadyProcessedMessage(msg)) return;
1219
1223
  const direction = msg.data.stepmode === 1 ? 'down' : 'up';
1220
1224
  const payload = {
1221
1225
  action: postfixWithEndpointName(`brightness_step_${direction}`, msg, model),
@@ -1242,7 +1246,7 @@ const converters = {
1242
1246
  type: ['commandStop', 'commandStopWithOnOff'],
1243
1247
  options: [exposes.options.simulated_brightness()],
1244
1248
  convert: (model, msg, publish, options, meta) => {
1245
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1249
+ if (hasAlreadyProcessedMessage(msg)) return;
1246
1250
  if (options.simulated_brightness) {
1247
1251
  clearInterval(globalStore.getValue(msg.endpoint, 'simulated_brightness_timer'));
1248
1252
  globalStore.putValue(msg.endpoint, 'simulated_brightness_timer', undefined);
@@ -1257,7 +1261,7 @@ const converters = {
1257
1261
  cluster: 'lightingColorCtrl',
1258
1262
  type: ['commandMoveColorTemp'],
1259
1263
  convert: (model, msg, publish, options, meta) => {
1260
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1264
+ if (hasAlreadyProcessedMessage(msg)) return;
1261
1265
  const direction = msg.data.movemode === 1 ? 'down' : 'up';
1262
1266
  const action = postfixWithEndpointName(`color_temperature_move_${direction}`, msg, model);
1263
1267
  const payload = {action, action_rate: msg.data.rate, action_minimum: msg.data.minimum, action_maximum: msg.data.maximum};
@@ -1269,7 +1273,7 @@ const converters = {
1269
1273
  cluster: 'lightingColorCtrl',
1270
1274
  type: 'commandStepColorTemp',
1271
1275
  convert: (model, msg, publish, options, meta) => {
1272
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1276
+ if (hasAlreadyProcessedMessage(msg)) return;
1273
1277
  const direction = msg.data.stepmode === 1 ? 'up' : 'down';
1274
1278
  const payload = {
1275
1279
  action: postfixWithEndpointName(`color_temperature_step_${direction}`, msg, model),
@@ -1288,7 +1292,7 @@ const converters = {
1288
1292
  cluster: 'lightingColorCtrl',
1289
1293
  type: 'commandEnhancedMoveToHueAndSaturation',
1290
1294
  convert: (model, msg, publish, options, meta) => {
1291
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1295
+ if (hasAlreadyProcessedMessage(msg)) return;
1292
1296
  const payload = {
1293
1297
  action: postfixWithEndpointName(`enhanced_move_to_hue_and_saturation`, msg, model),
1294
1298
  action_enhanced_hue: msg.data.enhancehue,
@@ -1305,7 +1309,7 @@ const converters = {
1305
1309
  cluster: 'lightingColorCtrl',
1306
1310
  type: ['commandStepHue'],
1307
1311
  convert: (model, msg, publish, options, meta) => {
1308
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1312
+ if (hasAlreadyProcessedMessage(msg)) return;
1309
1313
  const direction = msg.data.stepmode === 1 ? 'up' : 'down';
1310
1314
  const payload = {
1311
1315
  action: postfixWithEndpointName(`color_hue_step_${direction}`, msg, model),
@@ -1320,7 +1324,7 @@ const converters = {
1320
1324
  cluster: 'lightingColorCtrl',
1321
1325
  type: ['commandStepSaturation'],
1322
1326
  convert: (model, msg, publish, options, meta) => {
1323
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1327
+ if (hasAlreadyProcessedMessage(msg)) return;
1324
1328
  const direction = msg.data.stepmode === 1 ? 'up' : 'down';
1325
1329
  const payload = {
1326
1330
  action: postfixWithEndpointName(`color_saturation_step_${direction}`, msg, model),
@@ -1335,7 +1339,7 @@ const converters = {
1335
1339
  cluster: 'lightingColorCtrl',
1336
1340
  type: 'commandColorLoopSet',
1337
1341
  convert: (model, msg, publish, options, meta) => {
1338
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1342
+ if (hasAlreadyProcessedMessage(msg)) return;
1339
1343
  const updateFlags = msg.data.updateflags;
1340
1344
  const actionLookup = {
1341
1345
  0x00: 'deactivate',
@@ -1365,7 +1369,7 @@ const converters = {
1365
1369
  cluster: 'lightingColorCtrl',
1366
1370
  type: 'commandMoveToColorTemp',
1367
1371
  convert: (model, msg, publish, options, meta) => {
1368
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1372
+ if (hasAlreadyProcessedMessage(msg)) return;
1369
1373
  const payload = {
1370
1374
  action: postfixWithEndpointName(`color_temperature_move`, msg, model),
1371
1375
  action_color_temperature: msg.data.colortemp,
@@ -1379,7 +1383,7 @@ const converters = {
1379
1383
  cluster: 'lightingColorCtrl',
1380
1384
  type: 'commandMoveToColor',
1381
1385
  convert: (model, msg, publish, options, meta) => {
1382
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1386
+ if (hasAlreadyProcessedMessage(msg)) return;
1383
1387
  const payload = {
1384
1388
  action: postfixWithEndpointName(`color_move`, msg, model),
1385
1389
  action_color: {
@@ -1396,7 +1400,7 @@ const converters = {
1396
1400
  cluster: 'lightingColorCtrl',
1397
1401
  type: 'commandMoveHue',
1398
1402
  convert: (model, msg, publish, options, meta) => {
1399
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1403
+ if (hasAlreadyProcessedMessage(msg)) return;
1400
1404
  const movestop = msg.data.movemode == 1 ? 'move' : 'stop';
1401
1405
  const action = postfixWithEndpointName(`hue_${movestop}`, msg, model);
1402
1406
  const payload = {action, action_rate: msg.data.rate};
@@ -1408,7 +1412,7 @@ const converters = {
1408
1412
  cluster: 'lightingColorCtrl',
1409
1413
  type: 'commandMoveToSaturation',
1410
1414
  convert: (model, msg, publish, options, meta) => {
1411
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1415
+ if (hasAlreadyProcessedMessage(msg)) return;
1412
1416
  const payload = {
1413
1417
  action: postfixWithEndpointName('move_to_saturation', msg, model),
1414
1418
  action_saturation: msg.data.saturation,
@@ -1422,7 +1426,7 @@ const converters = {
1422
1426
  cluster: 'lightingColorCtrl',
1423
1427
  type: 'commandMoveToHue',
1424
1428
  convert: (model, msg, publish, options, meta) => {
1425
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1429
+ if (hasAlreadyProcessedMessage(msg)) return;
1426
1430
  const payload = {
1427
1431
  action: postfixWithEndpointName(`move_to_hue`, msg, model),
1428
1432
  action_hue: msg.data.hue,
@@ -1437,7 +1441,7 @@ const converters = {
1437
1441
  cluster: 'ssIasAce',
1438
1442
  type: 'commandEmergency',
1439
1443
  convert: (model, msg, publish, options, meta) => {
1440
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1444
+ if (hasAlreadyProcessedMessage(msg)) return;
1441
1445
  const payload = {action: postfixWithEndpointName(`emergency`, msg, model)};
1442
1446
  addActionGroup(payload, msg, model);
1443
1447
  return payload;
@@ -1447,7 +1451,7 @@ const converters = {
1447
1451
  cluster: 'genOnOff',
1448
1452
  type: 'commandOn',
1449
1453
  convert: (model, msg, publish, options, meta) => {
1450
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1454
+ if (hasAlreadyProcessedMessage(msg)) return;
1451
1455
  const property = postfixWithEndpointName('state', msg, model);
1452
1456
  return {[property]: 'ON'};
1453
1457
  },
@@ -1456,7 +1460,7 @@ const converters = {
1456
1460
  cluster: 'genOnOff',
1457
1461
  type: 'commandOff',
1458
1462
  convert: (model, msg, publish, options, meta) => {
1459
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
1463
+ if (hasAlreadyProcessedMessage(msg)) return;
1460
1464
  const property = postfixWithEndpointName('state', msg, model);
1461
1465
  return {[property]: 'OFF'};
1462
1466
  },
@@ -2181,7 +2185,7 @@ const converters = {
2181
2185
  cluster: 'ssIasZone',
2182
2186
  type: 'commandStatusChangeNotification',
2183
2187
  convert: (model, msg, publish, options, meta) => {
2184
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
2188
+ if (hasAlreadyProcessedMessage(msg)) return;
2185
2189
  const lookup = {1: 'pressed'};
2186
2190
  const zoneStatus = msg.data.zonestatus;
2187
2191
  return {
@@ -4045,7 +4049,8 @@ const converters = {
4045
4049
  exposes.options.precision('humidity'), exposes.options.calibration('humidity'),
4046
4050
  exposes.options.precision('co2'), exposes.options.calibration('co2'),
4047
4051
  exposes.options.precision('voc'), exposes.options.calibration('voc'),
4048
- exposes.options.precision('formaldehyd'), exposes.options.calibration('formaldehyd')],
4052
+ exposes.options.precision('formaldehyd'), exposes.options.calibration('formaldehyd'),
4053
+ exposes.options.precision('pm25'), exposes.options.calibration('pm25')],
4049
4054
  convert: (model, msg, publish, options, meta) => {
4050
4055
  const dpValue = tuya.firstDpValue(msg, meta, 'tuya_air_quality');
4051
4056
  const dp = dpValue.dp;
@@ -4062,6 +4067,8 @@ const converters = {
4062
4067
  case tuya.dataPoints.tuyaSabFormaldehyd:
4063
4068
  // Not sure which unit this is, supposedly mg/m³, but the value seems way too high.
4064
4069
  return {formaldehyd: calibrateAndPrecisionRoundOptions(value, options, 'formaldehyd')};
4070
+ case tuya.dataPoints.tuyaSahkMP25:
4071
+ return {pm25: calibrateAndPrecisionRoundOptions(value, options, 'pm25')};
4065
4072
  default:
4066
4073
  meta.logger.warn(`zigbee-herdsman-converters:TuyaSmartAirBox: Unrecognized DP #${
4067
4074
  dp} with data ${JSON.stringify(dpValue)}`);
@@ -4515,7 +4522,7 @@ const converters = {
4515
4522
  cluster: 'genScenes',
4516
4523
  type: 'commandTradfriArrowSingle',
4517
4524
  convert: (model, msg, publish, options, meta) => {
4518
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
4525
+ if (hasAlreadyProcessedMessage(msg)) return;
4519
4526
  if (msg.data.value === 2) {
4520
4527
  // This is send on toggle hold, ignore it as a toggle_hold is already handled above.
4521
4528
  return;
@@ -4529,7 +4536,7 @@ const converters = {
4529
4536
  cluster: 'genScenes',
4530
4537
  type: 'commandTradfriArrowHold',
4531
4538
  convert: (model, msg, publish, options, meta) => {
4532
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
4539
+ if (hasAlreadyProcessedMessage(msg)) return;
4533
4540
  const direction = msg.data.value === 3329 ? 'left' : 'right';
4534
4541
  globalStore.putValue(msg.endpoint, 'direction', direction);
4535
4542
  return {action: `arrow_${direction}_hold`};
@@ -4540,7 +4547,7 @@ const converters = {
4540
4547
  type: 'commandTradfriArrowRelease',
4541
4548
  options: [exposes.options.legacy()],
4542
4549
  convert: (model, msg, publish, options, meta) => {
4543
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
4550
+ if (hasAlreadyProcessedMessage(msg)) return;
4544
4551
  const direction = globalStore.getValue(msg.endpoint, 'direction');
4545
4552
  if (direction) {
4546
4553
  globalStore.clearValue(msg.endpoint, 'direction');
@@ -5250,19 +5257,27 @@ const converters = {
5250
5257
  else if (index === 5) {
5251
5258
  if (['JT-BZ-01AQ/A'].includes(model.model)) payload.power_outage_count = value;
5252
5259
  } else if (index === 100) {
5253
- if (['QBKG19LM', 'QBKG20LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM'].includes(model.model)) {
5260
+ if (['QBKG20LM', 'QBKG31LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM'].includes(model.model)) {
5254
5261
  const mapping = model.model === 'QBCZ15LM' ? 'relay' : 'left';
5255
5262
  payload[`state_${mapping}`] = value === 1 ? 'ON' : 'OFF';
5263
+ } else if (['WXKG14LM', 'WXKG16LM', 'WXKG17LM'].includes(model.model)) {
5264
+ payload.click_mode = {1: 'fast', 2: 'multi'}[value];
5256
5265
  } else {
5257
5266
  payload.state = value === 1 ? 'ON' : 'OFF';
5258
5267
  }
5259
5268
  } else if (index === 101) {
5260
- if (['QBKG19LM', 'QBKG20LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM'].includes(model.model)) {
5269
+ if (['QBKG20LM', 'QBKG31LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM'].includes(model.model)) {
5261
5270
  const mapping = model.model === 'QBCZ15LM' ? 'usb' : 'right';
5262
5271
  payload[`state_${mapping}`] = value === 1 ? 'ON' : 'OFF';
5272
+ } else if (['QBKG25LM', 'QBKG34LM'].includes(model.model)) {
5273
+ payload.state_center = value === 1 ? 'ON' : 'OFF';
5263
5274
  } else if (['RTCGQ12LM'].includes(model.model)) {
5264
5275
  payload.illuminance = calibrateAndPrecisionRoundOptions(value, options, 'illuminance');
5265
5276
  }
5277
+ } else if (index ===102 ) {
5278
+ if (['QBKG25LM', 'QBKG34LM'].includes(model.model)) {
5279
+ payload.state_right = value === 1 ? 'ON' : 'OFF';
5280
+ }
5266
5281
  } else if (index === 105) payload.motion_sensitivity = {1: 'low', 2: 'medium', 3: 'high'}[value]; // RTCGQ13LM
5267
5282
  else if (index === 149) {
5268
5283
  payload.energy = precisionRound(value, 2); // 0x95
@@ -5288,6 +5303,7 @@ const converters = {
5288
5303
  }
5289
5304
  if (msg.data.hasOwnProperty('4')) payload.mode_switch = {4: 'anti_flicker_mode', 1: 'quick_mode'}[msg.data['4']];
5290
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';
5291
5307
  if (msg.data.hasOwnProperty('258')) payload.detection_interval = msg.data['258'];
5292
5308
  if (msg.data.hasOwnProperty('268')) {
5293
5309
  if (['RTCGQ13LM'].includes(model.model)) {
@@ -5380,7 +5396,7 @@ const converters = {
5380
5396
  cluster: 'genMultistateInput',
5381
5397
  type: ['attributeReport'],
5382
5398
  convert: (model, msg, publish, options, meta) => {
5383
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5399
+ if (hasAlreadyProcessedMessage(msg)) return;
5384
5400
  let actionLookup = {0: 'hold', 1: 'single', 2: 'double', 3: 'triple', 255: 'release'};
5385
5401
  if (model.model === 'WXKG12LM') {
5386
5402
  actionLookup = {...actionLookup, 16: 'hold', 17: 'release', 18: 'shake'};
@@ -5526,7 +5542,7 @@ const converters = {
5526
5542
  `increase it with this option (value is in ms).`),
5527
5543
  ],
5528
5544
  convert: (model, msg, publish, options, meta) => {
5529
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5545
+ if (hasAlreadyProcessedMessage(msg)) return;
5530
5546
  const state = msg.data['onOff'];
5531
5547
 
5532
5548
  // 0 = click down, 1 = click up, else = multiple clicks
@@ -5832,7 +5848,7 @@ const converters = {
5832
5848
  cluster: 'genMultistateInput',
5833
5849
  type: ['attributeReport', 'readResponse'],
5834
5850
  convert: (model, msg, publish, options, meta) => {
5835
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5851
+ if (hasAlreadyProcessedMessage(msg)) return;
5836
5852
  const actionLookup = {0: 'hold', 255: 'release', 1: 'single', 2: 'double', 3: 'triple', 5: 'quintuple', 6: 'many'};
5837
5853
  const button = msg.endpoint.ID;
5838
5854
  const value = msg.data.presentValue;
@@ -5855,7 +5871,7 @@ const converters = {
5855
5871
  cluster: 'genOnOff',
5856
5872
  type: 'commandOn',
5857
5873
  convert: (model, msg, publish, options, meta) => {
5858
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5874
+ if (hasAlreadyProcessedMessage(msg)) return;
5859
5875
  return {action: 'button_2_single'};
5860
5876
  },
5861
5877
  },
@@ -5863,7 +5879,7 @@ const converters = {
5863
5879
  cluster: 'genOnOff',
5864
5880
  type: 'commandOff',
5865
5881
  convert: (model, msg, publish, options, meta) => {
5866
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5882
+ if (hasAlreadyProcessedMessage(msg)) return;
5867
5883
  return {action: 'button_1_single'};
5868
5884
  },
5869
5885
  },
@@ -5871,7 +5887,7 @@ const converters = {
5871
5887
  cluster: 'genLevelCtrl',
5872
5888
  type: 'commandStep',
5873
5889
  convert: (model, msg, publish, options, meta) => {
5874
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5890
+ if (hasAlreadyProcessedMessage(msg)) return;
5875
5891
  const button = msg.data.stepmode === 0 ? '4' : '3';
5876
5892
  return {action: `button_${button}_single`};
5877
5893
  },
@@ -5881,7 +5897,7 @@ const converters = {
5881
5897
  type: 'commandStop',
5882
5898
  options: [exposes.options.legacy()],
5883
5899
  convert: (model, msg, publish, options, meta) => {
5884
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5900
+ if (hasAlreadyProcessedMessage(msg)) return;
5885
5901
  if (globalStore.hasValue(msg.endpoint, 'button')) {
5886
5902
  const value = globalStore.getValue(msg.endpoint, 'button');
5887
5903
  const duration = Date.now() - value.start;
@@ -5895,7 +5911,7 @@ const converters = {
5895
5911
  cluster: 'genLevelCtrl',
5896
5912
  type: 'commandMove',
5897
5913
  convert: (model, msg, publish, options, meta) => {
5898
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5914
+ if (hasAlreadyProcessedMessage(msg)) return;
5899
5915
  const button = msg.data.movemode === 0 ? '4' : '3';
5900
5916
  globalStore.putValue(msg.endpoint, 'button', {button, start: Date.now()});
5901
5917
  return {action: `button_${button}_hold`};
@@ -5905,7 +5921,7 @@ const converters = {
5905
5921
  cluster: 'lightingColorCtrl',
5906
5922
  type: 'commandStepColorTemp',
5907
5923
  convert: (model, msg, publish, options, meta) => {
5908
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5924
+ if (hasAlreadyProcessedMessage(msg)) return;
5909
5925
  let action;
5910
5926
  if (model.model === 'WXCJKG12LM') {
5911
5927
  // for WXCJKG12LM model it's double click event on buttons 3 and 4
@@ -5922,7 +5938,7 @@ const converters = {
5922
5938
  type: 'commandMoveColorTemp',
5923
5939
  options: [exposes.options.legacy()],
5924
5940
  convert: (model, msg, publish, options, meta) => {
5925
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
5941
+ if (hasAlreadyProcessedMessage(msg)) return;
5926
5942
  const stop = msg.data.movemode === 0;
5927
5943
  let result = null;
5928
5944
  if (stop) {
@@ -7229,6 +7245,26 @@ const converters = {
7229
7245
  return {occupancy: (zoneStatus & 1) > 0, tamper: (zoneStatus & 4) > 0};
7230
7246
  },
7231
7247
  },
7248
+ ZM35HQ_attr: {
7249
+ cluster: 'ssIasZone',
7250
+ type: ['attributeReport', 'readResponse'],
7251
+ convert: (model, msg, publish, options, meta) => {
7252
+ let result = {};
7253
+ const data = msg.data;
7254
+ if (data && data.hasOwnProperty('zoneStatus')) {
7255
+ result = converters.ias_occupancy_alarm_1_report.convert(model, msg, publish, options, meta);
7256
+ }
7257
+ if (data && data.hasOwnProperty('currentZoneSensitivityLevel')) {
7258
+ const senslookup = {'0': 'low', '1': 'medium', '2': 'high'};
7259
+ result.sensitivity = senslookup[data.currentZoneSensitivityLevel];
7260
+ }
7261
+ if (data && data.hasOwnProperty('61441')) {
7262
+ const keeptimelookup = {'0': 30, '1': 60, '2': 120};
7263
+ result.keep_time = keeptimelookup[data['61441']];
7264
+ }
7265
+ return result;
7266
+ },
7267
+ },
7232
7268
  tuya_gas: {
7233
7269
  cluster: 'manuSpecificTuya',
7234
7270
  type: ['commandDataResponse'],
@@ -7328,7 +7364,7 @@ const converters = {
7328
7364
  cluster: 'wiserDeviceInfo',
7329
7365
  type: 'attributeReport',
7330
7366
  convert: (model, msg, publish, options, meta) => {
7331
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
7367
+ if (hasAlreadyProcessedMessage(msg)) return;
7332
7368
 
7333
7369
  const data = msg.data['deviceInfo'].split(',');
7334
7370
  if (data[0] === 'UI' && data[1]) {
@@ -8106,7 +8142,7 @@ const converters = {
8106
8142
  convert: (model, msg, publish, options, meta) => {
8107
8143
  // commandStopMove without params
8108
8144
  if (msg.data[2] !== 71) return;
8109
- if ((!model.meta || !model.meta.handleDuplicateTransaction) && hasAlreadyProcessedMessage(msg)) return;
8145
+ if (hasAlreadyProcessedMessage(msg)) return;
8110
8146
  const movestop = 'stop';
8111
8147
  const action = postfixWithEndpointName(`hue_${movestop}`, msg, model);
8112
8148
  const payload = {action};
@@ -1409,6 +1409,42 @@ const converters = {
1409
1409
  await entity.read('seMetering', ['instantaneousDemand']);
1410
1410
  },
1411
1411
  },
1412
+ currentsummdelivered: {
1413
+ key: ['energy'],
1414
+ convertGet: async (entity, key, meta) => {
1415
+ await entity.read('seMetering', ['currentSummDelivered']);
1416
+ },
1417
+ },
1418
+ frequency: {
1419
+ key: ['ac_frequency'],
1420
+ convertGet: async (entity, key, meta) => {
1421
+ await entity.read('haElectricalMeasurement', ['acFrequency']);
1422
+ },
1423
+ },
1424
+ powerfactor: {
1425
+ key: ['power_factor'],
1426
+ convertGet: async (entity, key, meta) => {
1427
+ await entity.read('haElectricalMeasurement', ['powerFactor']);
1428
+ },
1429
+ },
1430
+ acvoltage: {
1431
+ key: ['voltage'],
1432
+ convertGet: async (entity, key, meta) => {
1433
+ await entity.read('haElectricalMeasurement', ['rmsVoltage']);
1434
+ },
1435
+ },
1436
+ accurrent: {
1437
+ key: ['current'],
1438
+ convertGet: async (entity, key, meta) => {
1439
+ await entity.read('haElectricalMeasurement', ['rmsCurrent']);
1440
+ },
1441
+ },
1442
+ temperature: {
1443
+ key: ['temperature'],
1444
+ convertGet: async (entity, key, meta) => {
1445
+ await entity.read('msTemperatureMeasurement', ['measuredValue']);
1446
+ },
1447
+ },
1412
1448
  // #endregion
1413
1449
 
1414
1450
  // #region Non-generic converters
@@ -2137,7 +2173,7 @@ const converters = {
2137
2173
  key: ['led_disabled_night'],
2138
2174
  convertSet: async (entity, key, value, meta) => {
2139
2175
  if (['ZNCZ04LM', 'ZNCZ15LM', 'QBCZ14LM', 'QBCZ15LM', 'QBKG19LM', 'QBKG20LM', 'QBKG25LM', 'QBKG26LM',
2140
- 'QBKG34LM', 'DLKZMK11LM'].includes(meta.mapped.model)) {
2176
+ 'QBKG31LM', 'QBKG34LM', 'DLKZMK11LM'].includes(meta.mapped.model)) {
2141
2177
  await entity.write('aqaraOpple', {0x0203: {value: value ? 1 : 0, type: 0x10}}, manufacturerOptions.xiaomi);
2142
2178
  } else if (['ZNCZ11LM'].includes(meta.mapped.model)) {
2143
2179
  const payload = value ?
@@ -2152,13 +2188,24 @@ const converters = {
2152
2188
  },
2153
2189
  convertGet: async (entity, key, meta) => {
2154
2190
  if (['ZNCZ04LM', 'ZNCZ15LM', 'QBCZ15LM', 'QBCZ14LM', 'QBKG19LM', 'QBKG20LM', 'QBKG25LM', 'QBKG26LM',
2155
- 'QBKG34LM', 'DLKZMK11LM'].includes(meta.mapped.model)) {
2191
+ 'QBKG31LM', 'QBKG34LM', 'DLKZMK11LM'].includes(meta.mapped.model)) {
2156
2192
  await entity.read('aqaraOpple', [0x0203], manufacturerOptions.xiaomi);
2157
2193
  } else {
2158
2194
  throw new Error('Not supported');
2159
2195
  }
2160
2196
  },
2161
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
+ },
2162
2209
  xiaomi_switch_operation_mode_basic: {
2163
2210
  key: ['operation_mode'],
2164
2211
  convertSet: async (entity, key, value, meta) => {
@@ -5961,6 +6008,29 @@ const converters = {
5961
6008
  }
5962
6009
  },
5963
6010
  },
6011
+ ZM35HQ_attr: {
6012
+ key: [
6013
+ 'sensitivity', 'keep_time',
6014
+ ],
6015
+ convertSet: async (entity, key, value, meta) => {
6016
+ switch (key) {
6017
+ case 'sensitivity':
6018
+ await entity.write('ssIasZone', {currentZoneSensitivityLevel: {'low': 0, 'medium': 1, 'high': 2}[value]},
6019
+ {sendWhen: 'active'});
6020
+ break;
6021
+ case 'keep_time':
6022
+ await entity.write('ssIasZone', {61441: {value: {'30': 0, '60': 1, '120': 2}[value], type: 0x20}}, {sendWhen: 'active'});
6023
+ break;
6024
+ default: // Unknown key
6025
+ throw new Error(`Unhandled key ${key}`);
6026
+ }
6027
+ },
6028
+ convertGet: async (entity, key, meta) => {
6029
+ // Apparently, reading values may interfere with a commandStatusChangeNotification for changed occupancy.
6030
+ // Therefore, read "zoneStatus" as well.
6031
+ await entity.read('ssIasZone', ['currentZoneSensitivityLevel', 61441, 'zoneStatus'], {sendWhen: 'active'});
6032
+ },
6033
+ },
5964
6034
  TS0210_sensitivity: {
5965
6035
  key: ['sensitivity'],
5966
6036
  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',
package/devices/ikea.js CHANGED
@@ -746,10 +746,10 @@ module.exports = [
746
746
  extend: tradfriExtend.light_onoff_brightness_colortemp_color(),
747
747
  },
748
748
  {
749
- zigbeeModel: ['TRADFRIbulbE14WWclear250lm'],
749
+ zigbeeModel: ['TRADFRIbulbE14WWclear250lm', 'TRADFRIbulbE12WWclear250lm'],
750
750
  model: 'LED1935C3',
751
751
  vendor: 'IKEA',
752
- description: 'TRADFRI LED bulb E14 WW clear 250 lumen, dimmable',
752
+ description: 'TRADFRI LED bulb E12/E14 WW clear 250 lumen, dimmable',
753
753
  extend: tradfriExtend.light_onoff_brightness(),
754
754
  },
755
755
  {
@@ -15,6 +15,16 @@ const readInitialBatteryState = async (type, data, device) => {
15
15
  }
16
16
  };
17
17
 
18
+ const fzLocal = {
19
+ command_off: {
20
+ cluster: 'genOnOff',
21
+ type: 'commandOff',
22
+ convert: (model, msg, publish, options, meta) => {
23
+ return {action: 'off'};
24
+ },
25
+ },
26
+ };
27
+
18
28
  module.exports = [
19
29
  {
20
30
  zigbeeModel: [' Contactor\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000'+
@@ -107,11 +117,12 @@ module.exports = [
107
117
  vendor: 'Legrand',
108
118
  // led blink RED when battery is low
109
119
  description: 'Wireless remote switch',
110
- fromZigbee: [fz.identify, fz.command_on, fz.command_off, fz.command_toggle, fz.legacy.cmd_move, fz.legacy.cmd_stop, fz.battery],
120
+ fromZigbee: [fz.identify, fz.command_on, fzLocal.command_off, fz.command_toggle, fz.legacy.cmd_move, fz.legacy.cmd_stop,
121
+ fz.battery],
111
122
  exposes: [e.battery(), e.action(['identify', 'on', 'off', 'toggle', 'brightness_move_up',
112
123
  'brightness_move_down', 'brightness_stop'])],
113
124
  toZigbee: [],
114
- meta: {handleDuplicateTransaction: true, battery: {voltageToPercentage: '3V_2500'}},
125
+ meta: {battery: {voltageToPercentage: '3V_2500'}},
115
126
  configure: async (device, coordinatorEndpoint, logger) => {
116
127
  const endpoint = device.getEndpoint(1);
117
128
  await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'genOnOff', 'genLevelCtrl']);
@@ -123,11 +134,11 @@ module.exports = [
123
134
  model: '067774',
124
135
  vendor: 'Legrand',
125
136
  description: 'Wireless double remote switch',
126
- 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],
127
138
  exposes: [e.battery(),
128
139
  e.action(['identify', 'on', 'off', 'toggle', 'brightness_move_up', 'brightness_move_down', 'brightness_stop'])],
129
140
  toZigbee: [],
130
- meta: {handleDuplicateTransaction: true, multiEndpoint: true, battery: {voltageToPercentage: '3V_2500'}},
141
+ meta: {multiEndpoint: true, battery: {voltageToPercentage: '3V_2500'}},
131
142
  configure: async (device, coordinatorEndpoint, logger) => {
132
143
  const endpoint = device.getEndpoint(1);
133
144
  await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'genOnOff', 'genLevelCtrl']);
@@ -32,6 +32,26 @@ module.exports = [
32
32
  exposes.enum('backlight_mode', ea.ALL, ['LOW', 'MEDIUM', 'HIGH']),
33
33
  exposes.numeric('calibration_time', ea.STATE).withUnit('S').withDescription('Calibration time')],
34
34
  },
35
+ {
36
+ fingerprint: [{modelID: 'TS130F', manufacturerName: '_TZ3000_j1xl73iw'}],
37
+ model: 'TS130F_dual',
38
+ vendor: 'Lonsonho',
39
+ description: 'Dual curtain/blind module',
40
+ fromZigbee: [fz.cover_position_tilt, fz.tuya_cover_options],
41
+ toZigbee: [tz.cover_state, tz.cover_position_tilt, tz.tuya_cover_calibration, tz.tuya_cover_reversal],
42
+ meta: {multiEndpoint: true},
43
+ endpoint: (device) => {
44
+ return {'left': 1, 'right': 2};
45
+ },
46
+ exposes: [
47
+ exposes.enum('moving', ea.STATE, ['UP', 'STOP', 'DOWN']),
48
+ exposes.numeric('calibration_time', ea.STATE).withUnit('S').withDescription('Calibration time'),
49
+ e.cover_position().withEndpoint('left'), exposes.binary('calibration', ea.ALL, 'ON', 'OFF')
50
+ .withEndpoint('left'), exposes.binary('motor_reversal', ea.ALL, 'ON', 'OFF').withEndpoint('left'),
51
+ e.cover_position().withEndpoint('right'), exposes.binary('calibration', ea.ALL, 'ON', 'OFF')
52
+ .withEndpoint('right'), exposes.binary('motor_reversal', ea.ALL, 'ON', 'OFF').withEndpoint('right'),
53
+ ],
54
+ },
35
55
  {
36
56
  fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_8vxj8khv'}, {modelID: 'TS0601', manufacturerName: '_TZE200_7tdtqgwv'}],
37
57
  model: 'X711A',
@@ -177,4 +197,28 @@ module.exports = [
177
197
  await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff']);
178
198
  },
179
199
  },
200
+ {
201
+ fingerprint: [{modelID: 'TS110E', manufacturerName: '_TZ3210_zxbtub8r'}],
202
+ model: 'TS110E_1gang',
203
+ vendor: 'Lonsonho',
204
+ description: 'Zigbee smart dimmer module 1 gang with neutral',
205
+ extend: extend.light_onoff_brightness(),
206
+ },
207
+ {
208
+ fingerprint: [{modelID: 'TS110E', manufacturerName: '_TZ3210_wdexaypg'}],
209
+ model: 'TS110E_2gang',
210
+ vendor: 'Lonsonho',
211
+ description: 'Zigbee smart dimmer module 2 gang with neutral',
212
+ extend: extend.light_onoff_brightness({noConfigure: true}),
213
+ meta: {multiEndpoint: true},
214
+ exposes: [e.light_brightness().withEndpoint('l1'), e.light_brightness().withEndpoint('l2')],
215
+ configure: async (device, coordinatorEndpoint, logger) => {
216
+ await extend.light_onoff_brightness().configure(device, coordinatorEndpoint, logger);
217
+ await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff', 'genLevelCtrl']);
218
+ await reporting.bind(device.getEndpoint(2), coordinatorEndpoint, ['genOnOff', 'genLevelCtrl']);
219
+ },
220
+ endpoint: (device) => {
221
+ return {l1: 1, l2: 2};
222
+ },
223
+ },
180
224
  ];
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/namron.js CHANGED
@@ -220,6 +220,13 @@ module.exports = [
220
220
  description: 'LED 5,3W CCT E14',
221
221
  extend: extend.light_onoff_brightness_colortemp(),
222
222
  },
223
+ {
224
+ zigbeeModel: ['3802965'],
225
+ model: '3802965',
226
+ vendor: 'Namron',
227
+ description: 'LED 4,8W DIM GU10',
228
+ extend: extend.light_onoff_brightness(),
229
+ },
223
230
  {
224
231
  zigbeeModel: ['3802966'],
225
232
  model: '3802966',
@@ -40,7 +40,6 @@ module.exports = [
40
40
  description: 'Motion sensor',
41
41
  fromZigbee: [fz.battery, fz.ias_occupancy_alarm_1],
42
42
  toZigbee: [],
43
- meta: {battery: {dontDividePercentage: true}},
44
43
  exposes: [e.occupancy(), e.battery_low(), e.tamper(), e.battery()],
45
44
  configure: async (device, coordinatorEndpoint, logger) => {
46
45
  const endpoint = device.getEndpoint(1);
@@ -36,6 +36,15 @@ module.exports = [
36
36
  description: 'Hue white ambiance bathroom ceiling light Adore with Bluetooth',
37
37
  extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
38
38
  },
39
+ {
40
+ zigbeeModel: ['929003045301_01', '929003045301_02', '929003045301_03'],
41
+ model: '929003045301',
42
+ vendor: 'Philips',
43
+ description: 'Hue White and Color Ambiance GU10 (Centura)',
44
+ meta: {turnsOffAtBrightness1: true},
45
+ extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
46
+ ota: ota.zigbeeOTA,
47
+ },
39
48
  {
40
49
  zigbeeModel: ['929003056901'],
41
50
  model: '929003056901',
@@ -642,4 +642,35 @@ module.exports = [
642
642
  await reporting.currentSummDelivered(endpoint, {min: 60, change: 1});
643
643
  },
644
644
  },
645
+ {
646
+ zigbeeModel: ['NHMOTION/SWITCH/1'],
647
+ model: '545D6306',
648
+ vendor: 'Schneider Electric',
649
+ description: 'LK FUGA Wiser wireless PIR with relay',
650
+ fromZigbee: [fz.on_off, fz.illuminance, fz.occupancy, fz.occupancy_timeout],
651
+ exposes: [e.switch().withEndpoint('l1'), e.occupancy(), e.illuminance_lux(), e.illuminance(),
652
+ exposes.numeric('occupancy_timeout', ea.ALL).withUnit('second').withValueMin(0).withValueMax(3600)
653
+ .withDescription('Time in seconds after which occupancy is cleared after detecting it')],
654
+ toZigbee: [tz.on_off, tz.occupancy_timeout],
655
+ endpoint: (device) => {
656
+ return {'default': 37, 'l1': 1, 'l2': 37};
657
+ },
658
+ meta: {multiEndpoint: true},
659
+ configure: async (device, coordinatorEndpoint, logger) => {
660
+ const endpoint1 = device.getEndpoint(1);
661
+ const binds1 = ['genBasic', 'genIdentify', 'genOnOff'];
662
+ await reporting.bind(endpoint1, coordinatorEndpoint, binds1);
663
+ await reporting.onOff(endpoint1);
664
+ // read switch state
665
+ await endpoint1.read('genOnOff', ['onOff']);
666
+
667
+ const endpoint37 = device.getEndpoint(37);
668
+ const binds37 = ['msIlluminanceMeasurement', 'msOccupancySensing'];
669
+ await reporting.bind(endpoint37, coordinatorEndpoint, binds37);
670
+ await reporting.occupancy(endpoint37);
671
+ await reporting.illuminance(endpoint37);
672
+ // read occupancy_timeout
673
+ await endpoint37.read('msOccupancySensing', ['pirOToUDelay']);
674
+ },
675
+ },
645
676
  ];
@@ -183,7 +183,7 @@ module.exports = [
183
183
  toZigbee: [],
184
184
  configure: async (device, coordinatorEndpoint, logger) => {
185
185
  const endpoint = device.getEndpoint(1);
186
- await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
186
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'genPowerCfg']);
187
187
  await reporting.batteryVoltage(endpoint);
188
188
  },
189
189
  exposes: [e.battery(), e.battery_voltage(), e.action(['single', 'double', 'long'])],
@@ -230,11 +230,14 @@ module.exports = [
230
230
  toZigbee: [],
231
231
  exposes: [e.action(['1_single', '1_double', '1_long', '2_single', '2_double', '2_long',
232
232
  '3_single', '3_double', '3_long', '4_single', '4_double', '4_long']), e.battery(), e.battery_voltage()],
233
- meta: {battery: {voltageToPercentage: '3V_2100'}},
233
+ meta: {battery: {voltageToPercentage: '3V_2100'}, multiEndpoint: true},
234
234
  configure: async (device, coordinatorEndpoint, logger) => {
235
235
  const endpoint = device.getEndpoint(1);
236
- await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
236
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'genPowerCfg']);
237
237
  await reporting.batteryVoltage(endpoint, {min: 30, max: 21600, change: 1});
238
+ await reporting.bind(device.getEndpoint(2), coordinatorEndpoint, ['genOnOff']);
239
+ await reporting.bind(device.getEndpoint(3), coordinatorEndpoint, ['genOnOff']);
240
+ await reporting.bind(device.getEndpoint(4), coordinatorEndpoint, ['genOnOff']);
238
241
  },
239
242
  },
240
243
  {
@@ -247,7 +250,7 @@ module.exports = [
247
250
  toZigbee: [],
248
251
  configure: async (device, coordinatorEndpoint, logger) => {
249
252
  const endpoint = device.getEndpoint(1);
250
- await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
253
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'genPowerCfg']);
251
254
  await reporting.batteryVoltage(endpoint);
252
255
  },
253
256
  exposes: [e.battery(), e.battery_voltage(), e.action(['single', 'double', 'long'])],
@@ -260,12 +263,14 @@ module.exports = [
260
263
  fromZigbee: [fz.sihas_action, fz.battery],
261
264
  toZigbee: [],
262
265
  exposes: [e.action(['1_single', '1_double', '1_long', '2_single', '2_double', '2_long']), e.battery(), e.battery_voltage()],
263
- meta: {battery: {voltageToPercentage: '3V_2100'}},
266
+ meta: {battery: {voltageToPercentage: '3V_2100'}, multiEndpoint: true},
264
267
  configure: async (device, coordinatorEndpoint, logger) => {
265
268
  const endpoint = device.getEndpoint(1);
266
- await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
269
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'genPowerCfg']);
267
270
  await reporting.batteryVoltage(endpoint, {min: 30, max: 21600, change: 1});
271
+ await reporting.bind(device.getEndpoint(2), coordinatorEndpoint, ['genOnOff']);
268
272
  },
273
+
269
274
  },
270
275
  {
271
276
  zigbeeModel: ['SBM300ZB3'],
@@ -276,11 +281,13 @@ module.exports = [
276
281
  toZigbee: [],
277
282
  exposes: [e.action(['1_single', '1_double', '1_long', '2_single', '2_double', '2_long',
278
283
  '3_single', '3_double', '3_long']), e.battery(), e.battery_voltage()],
279
- meta: {battery: {voltageToPercentage: '3V_2100'}},
284
+ meta: {battery: {voltageToPercentage: '3V_2100'}, multiEndpoint: true},
280
285
  configure: async (device, coordinatorEndpoint, logger) => {
281
286
  const endpoint = device.getEndpoint(1);
282
- await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
287
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'genPowerCfg']);
283
288
  await reporting.batteryVoltage(endpoint, {min: 30, max: 21600, change: 1});
289
+ await reporting.bind(device.getEndpoint(2), coordinatorEndpoint, ['genOnOff']);
290
+ await reporting.bind(device.getEndpoint(3), coordinatorEndpoint, ['genOnOff']);
284
291
  },
285
292
  },
286
293
  {
@@ -300,4 +307,38 @@ module.exports = [
300
307
  await reporting.currentSummDelivered(endpoint, {min: 1, max: 600, change: 5});
301
308
  },
302
309
  },
310
+ {
311
+ zigbeeModel: ['PMM-300Z2'],
312
+ model: 'PMM-300Z2',
313
+ vendor: 'ShinaSystem',
314
+ description: 'SiHAS energy monitor',
315
+ fromZigbee: [fz.electrical_measurement, fz.metering, fz.temperature],
316
+ toZigbee: [tz.metering_power, tz.currentsummdelivered, tz.frequency, tz.powerfactor, tz.acvoltage, tz.accurrent, tz.temperature],
317
+ exposes: [e.power().withAccess(ea.STATE_GET), e.energy().withAccess(ea.STATE_GET),
318
+ e.current().withAccess(ea.STATE_GET), e.voltage().withAccess(ea.STATE_GET),
319
+ e.temperature().withAccess(ea.STATE_GET).withDescription('temperature of device internal mcu'),
320
+ exposes.numeric('power_factor', ea.STATE_GET).withDescription('Measured electrical power factor'),
321
+ exposes.numeric('ac_frequency', ea.STATE_GET).withUnit('Hz').withDescription('Measured electrical ac frequency')],
322
+ configure: async (device, coordinatorEndpoint, logger) => {
323
+ const endpoint = device.getEndpoint(1);
324
+ await reporting.bind(endpoint, coordinatorEndpoint, ['haElectricalMeasurement', 'seMetering', 'msTemperatureMeasurement']);
325
+ await endpoint.read('haElectricalMeasurement', ['acVoltageMultiplier', 'acVoltageDivisor', 'acCurrentMultiplier',
326
+ 'acCurrentDivisor']);
327
+ await endpoint.read('seMetering', ['multiplier', 'divisor']);
328
+ // await reporting.activePower(endpoint, {min: 1, max: 600, change: 5}); // no need, duplicate for power value.
329
+ await reporting.instantaneousDemand(endpoint, {min: 1, max: 600, change: 5});
330
+ await reporting.powerFactor(endpoint, {min: 10, max: 600, change: 1});
331
+ await reporting.rmsVoltage(endpoint, {min: 5, max: 600, change: 1});
332
+ await reporting.rmsCurrent(endpoint, {min: 5, max: 600, change: 1});
333
+ await reporting.currentSummDelivered(endpoint, {min: 1, max: 600, change: 5});
334
+ await reporting.temperature(endpoint, {min: 20, max: 300, change: 10});
335
+ endpoint.saveClusterAttributeKeyValue('haElectricalMeasurement', {acFrequencyMultiplier: 1, acFrequencyDivisor: 10});
336
+ await endpoint.configureReporting('haElectricalMeasurement', [{
337
+ attribute: 'acFrequency',
338
+ minimumReportInterval: 10,
339
+ maximumReportInterval: 600,
340
+ reportableChange: 3,
341
+ }]);
342
+ },
343
+ },
303
344
  ];
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: {
@@ -89,6 +89,15 @@ module.exports = [
89
89
  toZigbee: [],
90
90
  exposes: [e.temperature(), e.humidity(), e.co2(), e.voc(), e.formaldehyd()],
91
91
  },
92
+ {
93
+ fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_dwcarsat'}],
94
+ model: 'TS0601_smart_air_house_keeper',
95
+ vendor: 'TuYa',
96
+ description: 'Smart air house keeper',
97
+ fromZigbee: [fz.tuya_air_quality],
98
+ toZigbee: [],
99
+ exposes: [e.temperature(), e.humidity(), e.co2(), e.voc(), e.formaldehyd(), e.pm25()],
100
+ },
92
101
  {
93
102
  fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_ggev5fsl'}],
94
103
  model: 'TS0601_gas_sensor',
@@ -159,6 +168,7 @@ module.exports = [
159
168
  {modelID: 'TS0505B', manufacturerName: '_TZ3000_1mtktxdk'},
160
169
  {modelID: 'TS0505B', manufacturerName: '_TZ3210_remypqqm'},
161
170
  {modelID: 'TS0505B', manufacturerName: '_TZ3000_kohbva1f'},
171
+ {modelID: 'TS0505B', manufacturerName: '_TZ3210_wslkvrau'},
162
172
  {modelID: 'TS0505B', manufacturerName: '_TZ3210_0rn9qhnu'},
163
173
  {modelID: 'TS0505B', manufacturerName: '_TZ3210_bicjqpg4'},
164
174
  {modelID: 'TS0505B', manufacturerName: '_TZ3000_cmaky9gq'},
@@ -212,6 +222,7 @@ module.exports = [
212
222
  fingerprint: [{modelID: 'TS0501B', manufacturerName: '_TZ3000_4whigl8i'},
213
223
  {modelID: 'TS0501B', manufacturerName: '_TZ3210_4whigl8i'},
214
224
  {modelID: 'TS0501B', manufacturerName: '_TZ3210_9q49basr'},
225
+ {modelID: 'TS0501B', manufacturerName: '_TZ3210_i680rtja'},
215
226
  {modelID: 'TS0501B', manufacturerName: '_TZ3210_grnwgegn'},
216
227
  {modelID: 'TS0501B', manufacturerName: '_TZ3210_wuheofsg'},
217
228
  {modelID: 'TS0501B', manufacturerName: '_TZ3210_e5t9bfdv'}],
@@ -230,9 +241,7 @@ module.exports = [
230
241
  {modelID: 'TS0202', manufacturerName: '_TZ3000_mmtwjmaq'},
231
242
  {modelID: 'TS0202', manufacturerName: '_TZ3000_kmh5qpmb'},
232
243
  {modelID: 'TS0202', manufacturerName: '_TYZB01_zwvaj5wy'},
233
- {modelID: 'TS0202', manufacturerName: '_TZ3000_mcxw5ehu'},
234
244
  {modelID: 'TS0202', manufacturerName: '_TZ3000_bsvqrxru'},
235
- {modelID: 'TS0202', manufacturerName: '_TZ3000_msl6wxk9'},
236
245
  {modelID: 'TS0202', manufacturerName: '_TYZB01_tv3wxhcz'},
237
246
  {modelID: 'TS0202', manufacturerName: '_TYZB01_hqbdru35'},
238
247
  {modelID: 'TS0202', manufacturerName: '_TZ3000_tiwq83wk'},
@@ -245,6 +254,19 @@ module.exports = [
245
254
  toZigbee: [],
246
255
  exposes: [e.occupancy(), e.battery_low(), e.tamper(), e.battery()],
247
256
  },
257
+ {
258
+ fingerprint: [{modelID: 'TS0202', manufacturerName: '_TZ3000_mcxw5ehu'},
259
+ {modelID: 'TS0202', manufacturerName: '_TZ3000_msl6wxk9'}],
260
+ model: 'ZM-35H-Q',
261
+ vendor: 'TuYa',
262
+ description: 'Motion Sensor',
263
+ fromZigbee: [fz.ias_occupancy_alarm_1, fz.battery, fz.ignore_basic_report, fz.ZM35HQ_attr],
264
+ toZigbee: [tz.ZM35HQ_attr],
265
+ exposes: [e.occupancy(), e.battery_low(), e.tamper(), e.battery(),
266
+ exposes.enum('sensitivity', ea.ALL, ['low', 'medium', 'high']).withDescription('PIR sensor sensitivity'),
267
+ exposes.enum('keep_time', ea.ALL, ['30', '60', '120']).withDescription('PIR keep time in seconds'),
268
+ ],
269
+ },
248
270
  {
249
271
  fingerprint: [{modelID: 'TS0207', manufacturerName: '_TZ3000_m0vaazab'},
250
272
  {modelID: 'TS0207', manufacturerName: '_TZ3000_ufttklsz'},
@@ -326,8 +348,7 @@ module.exports = [
326
348
  ],
327
349
  },
328
350
  {
329
- fingerprint: [{modelID: 'TS011F', manufacturerName: '_TZ3000_oiymh3qu'},
330
- {modelID: 'TS011F', manufacturerName: '_TZ3000_o1jzcxou'}],
351
+ fingerprint: [{modelID: 'TS011F', manufacturerName: '_TZ3000_oiymh3qu'}],
331
352
  model: 'TS011F_socket_module',
332
353
  vendor: 'TuYa',
333
354
  description: 'Socket module',
@@ -336,12 +357,15 @@ module.exports = [
336
357
  },
337
358
  {
338
359
  fingerprint: [{modelID: 'TS011F', manufacturerName: '_TZ3000_wxtp7c5y'},
339
- {modelID: 'TS011F', manufacturerName: '_TYZB01_mtunwanm'}],
360
+ {modelID: 'TS011F', manufacturerName: '_TYZB01_mtunwanm'},
361
+ {modelID: 'TS011F', manufacturerName: '_TZ3000_o1jzcxou'}],
340
362
  model: 'TS011F_wall_outlet',
341
363
  vendor: 'TuYa',
342
364
  description: 'In-wall outlet',
343
365
  extend: extend.switch(),
344
- whiteLabel: [{vendor: 'Teekar', model: 'SWP86-01OG'}, {vendor: 'ClickSmart+', model: 'CMA30035'}],
366
+ whiteLabel: [{vendor: 'Teekar', model: 'SWP86-01OG'},
367
+ {vendor: 'ClickSmart+', model: 'CMA30035'},
368
+ {vendor: 'BSEED', model: 'Zigbee Socket'}],
345
369
  },
346
370
  {
347
371
  fingerprint: [{modelID: 'isltm67\u0000', manufacturerName: '_TYST11_pisltm67'}],
@@ -1139,6 +1163,7 @@ module.exports = [
1139
1163
  {
1140
1164
  fingerprint: [
1141
1165
  {modelID: 'TS011F', manufacturerName: '_TZ3000_hyfvrar3'},
1166
+ {modelID: 'TS011F', manufacturerName: '_TZ3000_v1pdxuqq'},
1142
1167
  {modelID: 'TS011F', manufacturerName: '_TZ3000_bfn1w0mm'}],
1143
1168
  model: 'TS011F_plug_2',
1144
1169
  description: 'Smart plug (without power monitoring)',
@@ -1590,6 +1615,7 @@ module.exports = [
1590
1615
  vendor: 'TuYa',
1591
1616
  description: 'Multiprise with 4 AC outlets and 2 USB super charging ports (16A)',
1592
1617
  extend: extend.switch(),
1618
+ fromZigbee: [fz.on_off_skip_duplicate_transaction],
1593
1619
  exposes: [e.switch().withEndpoint('l1'), e.switch().withEndpoint('l2'), e.switch().withEndpoint('l3'),
1594
1620
  e.switch().withEndpoint('l4')],
1595
1621
  whiteLabel: [{vendor: 'LEELKI', model: 'WP33-EU'}],
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
  },
@@ -736,9 +736,6 @@ module.exports = [
736
736
  exposes.enum('operation_mode', ea.ALL, ['control_relay', 'decoupled'])
737
737
  .withDescription('Decoupled mode for left button'),
738
738
  ],
739
- configure: async (device, coordinatorEndpoint, logger) => {
740
- await device.getEndpoint(1).write('aqaraOpple', {'mode': 1}, {manufacturerCode: 0x115f, disableResponse: true});
741
- },
742
739
  onEvent: preventReset,
743
740
  ota: ota.zigbeeOTA,
744
741
  },
@@ -767,9 +764,6 @@ module.exports = [
767
764
  .withDescription('Decoupled mode for right button')
768
765
  .withEndpoint('right'),
769
766
  ],
770
- configure: async (device, coordinatorEndpoint, logger) => {
771
- await device.getEndpoint(1).write('aqaraOpple', {'mode': 1}, {manufacturerCode: 0x115f, disableResponse: true});
772
- },
773
767
  onEvent: preventReset,
774
768
  ota: ota.zigbeeOTA,
775
769
  },
@@ -804,10 +798,8 @@ module.exports = [
804
798
  .withDescription('Decoupled mode for right button')
805
799
  .withEndpoint('right'),
806
800
  ],
807
- configure: async (device, coordinatorEndpoint, logger) => {
808
- await device.getEndpoint(1).write('aqaraOpple', {'mode': 1}, {manufacturerCode: 0x115f, disableResponse: true});
809
- },
810
801
  onEvent: preventReset,
802
+ ota: ota.zigbeeOTA,
811
803
  },
812
804
  {
813
805
  zigbeeModel: ['lumi.sens', 'lumi.sensor_ht'],
@@ -997,7 +989,7 @@ module.exports = [
997
989
  e.voltage().withAccess(ea.STATE), e.current(), e.consumer_connected(), e.led_disabled_night(),
998
990
  e.power_outage_memory(), exposes.binary('auto_off', ea.STATE_SET, true, false)
999
991
  .withDescription('Turn the device automatically off when attached device consumes less than 2W for 20 minutes'),
1000
- exposes.numeric('overload_protection', exposes.access.ALL).withValueMin(100).withValueMax(2200).withUnit('W')
992
+ exposes.numeric('overload_protection', exposes.access.ALL).withValueMin(100).withValueMax(2300).withUnit('W')
1001
993
  .withDescription('Maximum allowed load, turns off if exceeded')],
1002
994
  ota: ota.zigbeeOTA,
1003
995
  },
@@ -1691,7 +1683,7 @@ module.exports = [
1691
1683
  zigbeeModel: ['lumi.plug.macn01'],
1692
1684
  model: 'ZNCZ15LM',
1693
1685
  vendor: 'Xiaomi',
1694
- description: 'Aqara T1 power plug ZigBee',
1686
+ description: 'Aqara smart plug T1 (china standard)',
1695
1687
  fromZigbee: [fz.on_off, fz.xiaomi_power, fz.aqara_opple],
1696
1688
  toZigbee: [tz.on_off, tz.xiaomi_switch_power_outage_memory, tz.xiaomi_led_disabled_night,
1697
1689
  tz.xiaomi_overload_protection, tz.xiaomi_socket_button_lock],
@@ -1700,9 +1692,6 @@ module.exports = [
1700
1692
  e.power_outage_memory(), e.led_disabled_night(), e.button_lock(),
1701
1693
  exposes.numeric('overload_protection', exposes.access.ALL).withValueMin(100).withValueMax(2500).withUnit('W')
1702
1694
  .withDescription('Maximum allowed load, turns off if exceeded')],
1703
- configure: async (device, coordinatorEndpoint, logger) => {
1704
- await device.getEndpoint(1).write('aqaraOpple', {'mode': 1}, {manufacturerCode: 0x115f, disableResponse: true});
1705
- },
1706
1695
  ota: ota.zigbeeOTA,
1707
1696
  },
1708
1697
  {
@@ -1755,7 +1744,7 @@ module.exports = [
1755
1744
  zigbeeModel: ['lumi.plug.sacn03'],
1756
1745
  model: 'QBCZ15LM',
1757
1746
  vendor: 'Xiaomi',
1758
- description: 'Aqara smart wall outlet H1 usb',
1747
+ description: 'Aqara smart wall outlet H1 (USB)',
1759
1748
  fromZigbee: [fz.on_off, fz.xiaomi_power, fz.aqara_opple],
1760
1749
  toZigbee: [tz.on_off, tz.xiaomi_switch_power_outage_memory, tz.xiaomi_led_disabled_night,
1761
1750
  tz.xiaomi_button_switch_mode, tz.xiaomi_overload_protection, tz.xiaomi_socket_button_lock],
@@ -1771,9 +1760,6 @@ module.exports = [
1771
1760
  .withDescription('Control both relay and usb or only the relay with the physical switch button'),
1772
1761
  exposes.numeric('overload_protection', exposes.access.ALL).withValueMin(100).withValueMax(2500).withUnit('W')
1773
1762
  .withDescription('Maximum allowed load, turns off if exceeded')],
1774
- configure: async (device, coordinatorEndpoint, logger) => {
1775
- await device.getEndpoint(1).write('aqaraOpple', {'mode': 1}, {manufacturerCode: 0x115f, disableResponse: true});
1776
- },
1777
1763
  ota: ota.zigbeeOTA,
1778
1764
  },
1779
1765
  {
@@ -1805,9 +1791,6 @@ module.exports = [
1805
1791
  e.current(), e.power_outage_memory(), e.led_disabled_night(), e.button_lock(),
1806
1792
  exposes.numeric('overload_protection', exposes.access.ALL).withValueMin(100).withValueMax(2500).withUnit('W')
1807
1793
  .withDescription('Maximum allowed load, turns off if exceeded')],
1808
- configure: async (device, coordinatorEndpoint, logger) => {
1809
- await device.getEndpoint(1).write('aqaraOpple', {'mode': 1}, {manufacturerCode: 0x115f, disableResponse: true});
1810
- },
1811
1794
  ota: ota.zigbeeOTA,
1812
1795
  },
1813
1796
  {
@@ -1873,7 +1856,7 @@ module.exports = [
1873
1856
  model: 'WXKG14LM',
1874
1857
  vendor: 'Xiaomi',
1875
1858
  description: 'Aqara wireless remote switch H1 (single rocker)',
1876
- fromZigbee: [fz.xiaomi_multistate_action, fz.aqara_opple],
1859
+ fromZigbee: [fz.xiaomi_multistate_action, fz.aqara_opple, fz.command_toggle],
1877
1860
  toZigbee: [tz.xiaomi_switch_click_mode, tz.aqara_opple_operation_mode],
1878
1861
  exposes: [e.battery(), e.battery_voltage(), e.action(['single', 'double', 'triple', 'hold']),
1879
1862
  exposes.enum('click_mode', ea.ALL, ['fast', 'multi'])
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'),
package/lib/tuya.js CHANGED
@@ -418,6 +418,9 @@ const dataPoints = {
418
418
  tuyaSabHumidity: 19,
419
419
  tuyaSabVOC: 21,
420
420
  tuyaSabFormaldehyd: 22,
421
+ // tuya Smart Air House Keeper, Multifunctionale air quality detector.
422
+ // CO2, Temp, Humidity, VOC and Formaldehyd same as Smart Air Box
423
+ tuyaSahkMP25: 20,
421
424
  lidlTimer: 5,
422
425
  // Moes MS-105 Dimmer
423
426
  moes105DimmerState1: 1,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "14.0.399",
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.399",
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": [