zigbee-herdsman-converters 14.0.633 → 14.0.635

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/devices/tuya.js CHANGED
@@ -908,7 +908,8 @@ module.exports = [
908
908
  {modelID: 'TS0505B', manufacturerName: '_TZ3210_6amjviba'},
909
909
  {modelID: 'TS0505B', manufacturerName: '_TZ3000_xr5m6kfg'},
910
910
  {modelID: 'TS0505B', manufacturerName: '_TZ3210_xr5m6kfg'},
911
- {modelID: 'TS0505B', manufacturerName: '_TZ3210_bf175wi4'}],
911
+ {modelID: 'TS0505B', manufacturerName: '_TZ3210_bf175wi4'},
912
+ {modelID: 'TS0505B', manufacturerName: '_TZB210_3zfp8mki'}],
912
913
  model: 'TS0505B',
913
914
  vendor: 'TuYa',
914
915
  description: 'Zigbee RGB+CCT light',
@@ -1138,8 +1139,6 @@ module.exports = [
1138
1139
  {modelID: 'TS0601', manufacturerName: '_TZE200_ojzhk75b'},
1139
1140
  {modelID: 'TS0601', manufacturerName: '_TZE200_swaamsoy'},
1140
1141
  {modelID: 'TS0601', manufacturerName: '_TZE200_3p5ydos3'},
1141
- {modelID: 'TS0601', manufacturerName: '_TZE200_1agwnems'},
1142
- {modelID: 'TS0601', manufacturerName: '_TZE200_ip2akl4w'},
1143
1142
  ],
1144
1143
  model: 'TS0601_dimmer',
1145
1144
  vendor: 'TuYa',
@@ -1163,6 +1162,27 @@ module.exports = [
1163
1162
  {vendor: 'Moes', model: 'EDM-1ZBB-EU'},
1164
1163
  ],
1165
1164
  },
1165
+ {
1166
+ fingerprint: tuya.fingerprint('TS0601', ['_TZE200_ip2akl4w', '_TZE200_1agwnems']),
1167
+ model: 'TS0601_dimmer_1',
1168
+ vendor: 'TuYa',
1169
+ description: 'Zigbee smart dimmer',
1170
+ fromZigbee: [tuya.fzDataPoints],
1171
+ toZigbee: [tuya.tzDataPoints],
1172
+ configure: tuya.configureMagicPacket,
1173
+ exposes: [tuya.exposes.lightBrightnessWithMinMax, tuya.exposes.powerOnBehavior, tuya.exposes.countdown, tuya.exposes.lightType],
1174
+ meta: {
1175
+ tuyaDatapoints: [
1176
+ [1, 'state', tuya.valueConverter.onOff, {skip: tuya.skip.stateOnAndBrightnessPresent}],
1177
+ [2, 'brightness', tuya.valueConverter.scale0_254to0_1000],
1178
+ [3, 'min_brightness', tuya.valueConverter.scale0_254to0_1000],
1179
+ [4, 'light_type', tuya.valueConverter.lightType],
1180
+ [5, 'max_brightness', tuya.valueConverter.scale0_254to0_1000],
1181
+ [6, 'countdown', tuya.valueConverter.countdown],
1182
+ [14, 'power_on_behavior', tuya.valueConverter.powerOnBehavior],
1183
+ ],
1184
+ },
1185
+ },
1166
1186
  {
1167
1187
  fingerprint: [{modelID: 'TS011F', manufacturerName: '_TZ3000_oiymh3qu'}],
1168
1188
  model: 'TS011F_socket_module',
package/lib/tuya.js CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  const constants = require('./constants');
4
4
  const globalStore = require('./store');
5
+ const exposes = require('./exposes');
6
+ const utils = require('./utils');
7
+ const e = exposes.presets;
8
+ const ea = exposes.access;
5
9
 
6
10
  const dataTypes = {
7
11
  raw: 0, // [ bytes ]
@@ -1131,7 +1135,132 @@ async function sendDataPointStringBuffer(entity, dp, value, cmd, seq=undefined)
1131
1135
  );
1132
1136
  }
1133
1137
 
1138
+ const tuyaExposes = {
1139
+ powerOnBehavior: exposes.enum('power_on_behavior', ea.STATE_SET, ['off', 'on', 'previous'])
1140
+ .withDescription('Controls the behavior when the device is powered on'),
1141
+ lightType: exposes.enum('light_type', ea.STATE_SET, ['led', 'incandescent', 'halogen'])
1142
+ .withDescription('Type of light attached to the device'),
1143
+ lightBrightnessWithMinMax: e.light_brightness().withMinBrightness().withMaxBrightness().setAccess(
1144
+ 'state', ea.STATE_SET).setAccess('brightness', ea.STATE_SET).setAccess(
1145
+ 'min_brightness', ea.STATE_SET).setAccess('max_brightness', ea.STATE_SET),
1146
+ countdown: exposes.numeric('countdown', ea.STATE_SET).withValueMin(0).withValueMax(43200).withValueStep(1).withUnit('s')
1147
+ .withDescription('Countdown to turn device off after a certain time'),
1148
+ };
1149
+
1150
+ const skip = {
1151
+ // Prevent state from being published when already ON and brightness is also published.
1152
+ // This prevents 100% -> X% brightness jumps when the switch is already on
1153
+ // https://github.com/Koenkk/zigbee2mqtt/issues/13800#issuecomment-1263592783
1154
+ stateOnAndBrightnessPresent: (meta) => meta.message.hasOwnProperty('brightness') && meta.state.state === 'ON',
1155
+ };
1156
+
1157
+ const configureMagicPacket = async (device, coordinatorEndpoint, logger) => {
1158
+ const endpoint = device.endpoints[0];
1159
+ await endpoint.read('genBasic', ['manufacturerName', 'zclVersion', 'appVersion', 'modelId', 'powerSource', 0xfffe]);
1160
+ };
1161
+
1162
+ const fingerprint = (modelID, manufacturerNames) => {
1163
+ return manufacturerNames.map((manufacturerName) => {
1164
+ return {modelID, manufacturerName};
1165
+ });
1166
+ };
1167
+
1168
+ const valueConverterBasic = {
1169
+ lookup: (map) => {
1170
+ return {
1171
+ to: (v) => {
1172
+ if (map[v] === undefined) throw new Error(`Value '${v}' is not allowed, expected one of ${Object.keys(map)}`);
1173
+ return map[v];
1174
+ },
1175
+ from: (v) => {
1176
+ const value = Object.entries(map).find((i) => i[1] === v);
1177
+ if (!value) throw new Error(`Value '${v}' is not allowed, expected one of ${Object.values(map)}`);
1178
+ return value[0];
1179
+ },
1180
+ };
1181
+ },
1182
+ scale: (min1, max1, min2, max2) => {
1183
+ return {to: (v) => utils.mapNumberRange(v, min1, max1, min2, max2), from: (v) => utils.mapNumberRange(v, min2, max2, min1, max1)};
1184
+ },
1185
+ raw: () => {
1186
+ return {to: (v) => v, from: (v) => v};
1187
+ },
1188
+ divideBy: (value) => {
1189
+ return {to: (v) => v * value, from: (v) => v / value};
1190
+ },
1191
+ };
1192
+
1193
+ const valueConverter = {
1194
+ trueFalse: valueConverterBasic.lookup({1: true, 0: false}),
1195
+ onOff: valueConverterBasic.lookup({'ON': true, 'OFF': false}),
1196
+ powerOnBehavior: valueConverterBasic.lookup({'off': 0, 'on': 1, 'previous': 2}),
1197
+ lightType: valueConverterBasic.lookup({'led': 0, 'incandescent': 1, 'halogen': 2}),
1198
+ countdown: valueConverterBasic.raw(),
1199
+ scale0_254to0_1000: valueConverterBasic.scale(0, 254, 0, 1000),
1200
+ scale0_1to0_1000: valueConverterBasic.scale(0, 1, 0, 1000),
1201
+ divideBy100: valueConverterBasic.divideBy(100),
1202
+ };
1203
+
1204
+ const tzDataPoints = {
1205
+ key: ['state', 'brightness', 'min_brightness', 'max_brightness', 'power_on_behavior', 'countdown', 'light_type'],
1206
+ convertSet: async (entity, key, value, meta) => {
1207
+ // A set converter is only called once; therefore we need to loop
1208
+ const state = {};
1209
+ if (!meta.mapped.meta || !meta.mapped.meta.tuyaDatapoints) throw new Error('No datapoints map defined');
1210
+ const datapoints = meta.mapped.meta.tuyaDatapoints;
1211
+ for (const [key, value] of Object.entries(meta.message)) {
1212
+ const convertedKey = meta.mapped.meta.multiEndpoint ? `${key}_${meta.endpoint_name}` : key;
1213
+ const dpEntry = datapoints.find((d) => d[1] === convertedKey);
1214
+ if (!dpEntry || !dpEntry[1]) {
1215
+ throw new Error(`No datapoint defined for '${key}'`);
1216
+ }
1217
+ if (dpEntry[3] && dpEntry[3].skip && dpEntry[3].skip(meta)) continue;
1218
+ const dpId = dpEntry[0];
1219
+ const convertedValue = dpEntry[2].to(value);
1220
+ if (typeof convertedValue === 'boolean') {
1221
+ await sendDataPointBool(entity, dpId, convertedValue, 'dataRequest', 1);
1222
+ } else if (typeof convertedValue === 'number') {
1223
+ await sendDataPointValue(entity, dpId, convertedValue, 'dataRequest', 1);
1224
+ } else {
1225
+ throw new Error(`Don't know how to send type '${typeof convertedValue}'`);
1226
+ }
1227
+ state[convertedKey] = value;
1228
+ }
1229
+ return {state};
1230
+ },
1231
+ };
1232
+
1233
+ const fzDataPoints = {
1234
+ cluster: 'manuSpecificTuya',
1235
+ type: ['commandDataResponse', 'commandDataReport'],
1236
+ convert: (model, msg, publish, options, meta) => {
1237
+ const result = {};
1238
+ if (!model.meta || !model.meta.tuyaDatapoints) throw new Error('No datapoints map defined');
1239
+ const datapoints = model.meta.tuyaDatapoints;
1240
+ for (const dpValue of msg.data.dpValues) {
1241
+ const dpId = dpValue.dp;
1242
+ const dpEntry = datapoints.find((d) => d[0] === dpId);
1243
+ if (dpEntry) {
1244
+ const value = getDataValue(dpValue);
1245
+ result[dpEntry[1]] = dpEntry[2].from(value);
1246
+ } else {
1247
+ meta.logger.warn(`Datapoint ${dpId} not defined for '${meta.device.manufacturerName}' ` +
1248
+ `with data ${JSON.stringify(dpValue)}`);
1249
+ }
1250
+ }
1251
+ return result;
1252
+ },
1253
+ };
1254
+
1134
1255
  module.exports = {
1256
+ exposes: tuyaExposes,
1257
+ skip,
1258
+ configureMagicPacket,
1259
+ fingerprint,
1260
+ valueConverterBasic,
1261
+ valueConverter,
1262
+ tzDataPoints,
1263
+ fzDataPoints,
1135
1264
  sendDataPoint,
1136
1265
  sendDataPoints,
1137
1266
  sendDataPointValue,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "14.0.633",
3
+ "version": "14.0.635",
4
4
  "description": "Collection of device converters to be used with zigbee-herdsman",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -38,7 +38,7 @@
38
38
  "buffer-crc32": "^0.2.13",
39
39
  "https-proxy-agent": "^5.0.1",
40
40
  "tar-stream": "^2.2.0",
41
- "zigbee-herdsman": "^0.14.59"
41
+ "zigbee-herdsman": "^0.14.62"
42
42
  },
43
43
  "devDependencies": {
44
44
  "eslint": "*",