zigbee-herdsman-converters 15.7.1 → 15.8.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## [15.8.2](https://github.com/Koenkk/zigbee-herdsman-converters/compare/v15.8.1...v15.8.2) (2023-05-24)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **ignore:** ci.yml ([e7c1d90](https://github.com/Koenkk/zigbee-herdsman-converters/commit/e7c1d90e2826fc88354df66872ff978571347d61))
9
+
10
+ ## [15.8.1](https://github.com/Koenkk/zigbee-herdsman-converters/compare/v15.8.0...v15.8.1) (2023-05-24)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **ignore:** fix ci.yml ([38cda3a](https://github.com/Koenkk/zigbee-herdsman-converters/commit/38cda3afa354620fc2888bc65d5f764a7f6ffa8e))
16
+
17
+ ## [15.8.0](https://github.com/Koenkk/zigbee-herdsman-converters/compare/v15.7.1...v15.8.0) (2023-05-24)
18
+
19
+
20
+ ### Features
21
+
22
+ * **add:** 9135 [@anharald](https://github.com/anharald) https://github.com/Koenkk/zigbee2mqtt/issues/17786 ([16ba1db](https://github.com/Koenkk/zigbee-herdsman-converters/commit/16ba1db909eb78b0c3c1e1e1403294c019432ab8))
23
+ * **add:** S902M-ZG https://github.com/Koenkk/zigbee2mqtt/issues/14733 ([2b45558](https://github.com/Koenkk/zigbee-herdsman-converters/commit/2b45558cebb255d339573149ee630d677a8cf7fc))
24
+ * **add:** SM0202 https://github.com/Koenkk/zigbee2mqtt/issues/15772 ([fcb9c1e](https://github.com/Koenkk/zigbee-herdsman-converters/commit/fcb9c1e5d93f9f550c16e542e945a74c2fac5272))
25
+ * Improvements for Custom devices (DiY) ptvo.info ([#5792](https://github.com/Koenkk/zigbee-herdsman-converters/issues/5792)) ([6f2b2bc](https://github.com/Koenkk/zigbee-herdsman-converters/commit/6f2b2bc904e668103acc9b6b9f67699e6c556553))
26
+
3
27
  ## [15.7.1](https://github.com/Koenkk/zigbee-herdsman-converters/compare/v15.7.0...v15.7.1) (2023-05-24)
4
28
 
5
29
 
package/README.md CHANGED
@@ -43,3 +43,5 @@ If any of those commands finish with an error your PR won't pass the tests and w
43
43
  - `{dontDividePercentage: true}`: prevents batteryPercentageRemainig from being divided (ZCL 200=100%, but some report 100=100%) (default: false)
44
44
  - `{voltageToPercentage: '3V_2100'}`: convert voltage to percentage using specified option. See utils.batteryVoltageToPercentage() (default: null, no voltage to percentage conversion)
45
45
  - `fanStateOn`: value used for fan_mode when using fan_state="ON", the default is "on"
46
+
47
+
@@ -156,94 +156,6 @@ declare const _exports: ({
156
156
  })[];
157
157
  toZigbee: undefined[];
158
158
  configure: (device: any, coordinatorEndpoint: any, logger: any) => Promise<void>;
159
- } | {
160
- fingerprint: {
161
- modelID: string;
162
- manufacturerName: string;
163
- }[];
164
- model: string;
165
- vendor: string;
166
- description: string;
167
- fromZigbee: ({
168
- cluster: string;
169
- type: string;
170
- options: {
171
- type: string;
172
- name: any;
173
- property: any;
174
- access: any;
175
- withUnit(unit: any): any;
176
- unit: any;
177
- withValueMax(value: any): any;
178
- value_max: any;
179
- withValueMin(value: any): any;
180
- value_min: any;
181
- withValueStep(value: any): any;
182
- value_step: any;
183
- withPreset(name: any, value: any, description: any): any;
184
- presets: any[];
185
- withEndpoint(endpointName: any): any;
186
- endpoint: any;
187
- withAccess(a: any): any;
188
- withProperty(property: any): any;
189
- withDescription(description: any): any;
190
- description: any;
191
- removeFeature(feature: any): any;
192
- setAccess(feature: any, a: any): any;
193
- }[];
194
- convert: (model: any, msg: any, publish: any, options: any, meta: any) => {
195
- occupancy: boolean;
196
- tamper: boolean;
197
- battery_low: boolean;
198
- };
199
- } | {
200
- cluster: string;
201
- type: string[];
202
- convert: (model: any, msg: any, publish: any, options: any, meta: any) => any;
203
- })[];
204
- toZigbee: undefined[];
205
- exposes: ({
206
- type: string;
207
- name: any;
208
- property: any;
209
- access: any;
210
- value_on: any;
211
- value_off: any;
212
- withValueToggle(value: any): any;
213
- value_toggle: any;
214
- withEndpoint(endpointName: any): any;
215
- endpoint: any;
216
- withAccess(a: any): any;
217
- withProperty(property: any): any;
218
- withDescription(description: any): any;
219
- description: any;
220
- removeFeature(feature: any): any;
221
- setAccess(feature: any, a: any): any;
222
- } | {
223
- type: string;
224
- name: any;
225
- property: any;
226
- access: any;
227
- withUnit(unit: any): any;
228
- unit: any;
229
- withValueMax(value: any): any;
230
- value_max: any;
231
- withValueMin(value: any): any;
232
- value_min: any;
233
- withValueStep(value: any): any;
234
- value_step: any;
235
- withPreset(name: any, value: any, description: any): any;
236
- presets: any[];
237
- withEndpoint(endpointName: any): any;
238
- endpoint: any;
239
- withAccess(a: any): any;
240
- withProperty(property: any): any;
241
- withDescription(description: any): any;
242
- description: any;
243
- removeFeature(feature: any): any;
244
- setAccess(feature: any, a: any): any;
245
- })[];
246
- configure: (device: any, coordinatorEndpoint: any, logger: any) => Promise<void>;
247
159
  })[];
248
160
  export = _exports;
249
161
  //# sourceMappingURL=cleverio.d.ts.map
@@ -32,19 +32,5 @@ module.exports = [
32
32
  toZigbee: [],
33
33
  configure: tuya.configureMagicPacket,
34
34
  },
35
- {
36
- fingerprint: [{ modelID: 'SM0202', manufacturerName: '_TYZB01_z2umiwvq' }],
37
- model: 'SS200',
38
- vendor: 'Cleverio',
39
- description: 'Smart motion sensor',
40
- fromZigbee: [fz.ias_occupancy_alarm_1_with_timeout, fz.battery, fz.ignore_basic_report],
41
- toZigbee: [],
42
- exposes: [e.occupancy(), e.battery_low(), e.linkquality(), e.battery(), e.battery_voltage()],
43
- configure: async (device, coordinatorEndpoint, logger) => {
44
- const endpoint = device.getEndpoint(1);
45
- await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
46
- await reporting.batteryPercentageRemaining(endpoint);
47
- },
48
- },
49
35
  ];
50
36
  //# sourceMappingURL=cleverio.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cleverio.js","sourceRoot":"","sources":["../src/devices/cleverio.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cleverio.js","sourceRoot":"","sources":["../src/devices/cleverio.js"],"names":[],"mappings":"AAAA,MAAM,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAC1C,MAAM,EAAE,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAAC;AAC/C,MAAM,EAAE,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;AAC7C,MAAM,SAAS,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAC9C,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;AACpC,MAAM,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;AAC1B,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;AAE1B,MAAM,CAAC,OAAO,GAAG;IACb;QACI,WAAW,EAAE,CAAC,EAAC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,kBAAkB,EAAC,CAAC;QACxE,KAAK,EAAE,OAAO;QACd,MAAM,EAAE,UAAU;QAClB,WAAW,EAAE,aAAa;QAC1B,UAAU,EAAE,CAAC,EAAE,CAAC,YAAY,EAAE,EAAE,CAAC,sBAAsB,EAAE,EAAE,CAAC,YAAY,CAAC;QACzE,QAAQ,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,aAAa,CAAC;QACxC,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC;YACjE,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,eAAe,CAAC,iBAAiB,CAAC,CAAC;QAC3G,IAAI,EAAE,EAAC,sBAAsB,EAAE,IAAI,EAAC;QACpC,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,EAAE;YAC7C,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;YACvC,MAAM,YAAY,GAAG,CAAC,aAAa,CAAC,CAAC;YACrC,MAAM,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,mBAAmB,EAAE,YAAY,CAAC,CAAC;QACtE,CAAC;KACJ;IACD;QACI,WAAW,EAAE,CAAC,EAAC,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,kBAAkB,EAAC,CAAC;QACzE,KAAK,EAAE,OAAO;QACd,MAAM,EAAE,UAAU;QAClB,WAAW,EAAE,+BAA+B;QAC5C,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;QAC9D,UAAU,EAAE,CAAC,EAAE,CAAC,kBAAkB,EAAE,EAAE,CAAC,OAAO,CAAC;QAC/C,QAAQ,EAAE,EAAE;QACZ,SAAS,EAAE,IAAI,CAAC,oBAAoB;KACvC;CACJ,CAAC"}
@@ -172,11 +172,26 @@ declare const _exports: ({
172
172
  }>;
173
173
  convertGet: (entity: any, key: any, meta: any) => Promise<void>;
174
174
  })[];
175
- exposes: {
175
+ exposes: (device: any, options: any) => ({
176
+ type: string;
177
+ features: any[];
178
+ withState(property: any, toggle: any, description: any, access?: number, value_on?: string, value_off?: string): any;
179
+ withEndpoint(endpointName: any): any;
180
+ endpoint: any;
181
+ property: any;
182
+ withAccess(a: any): any;
183
+ access: any;
184
+ withProperty(property: any): any;
185
+ withDescription(description: any): any;
186
+ description: any;
187
+ removeFeature(feature: any): any;
188
+ setAccess(feature: any, a: any): any;
189
+ } | {
176
190
  type: string;
177
191
  name: any;
178
192
  property: any;
179
193
  access: any;
194
+ values: any;
180
195
  withEndpoint(endpointName: any): any;
181
196
  endpoint: any;
182
197
  withAccess(a: any): any;
@@ -185,7 +200,43 @@ declare const _exports: ({
185
200
  description: any;
186
201
  removeFeature(feature: any): any;
187
202
  setAccess(feature: any, a: any): any;
188
- }[];
203
+ } | {
204
+ type: string;
205
+ name: any;
206
+ property: any;
207
+ access: any;
208
+ withUnit(unit: any): any;
209
+ unit: any;
210
+ withValueMax(value: any): any;
211
+ value_max: any;
212
+ withValueMin(value: any): any;
213
+ value_min: any;
214
+ withValueStep(value: any): any;
215
+ value_step: any;
216
+ withPreset(name: any, value: any, description: any): any;
217
+ presets: any[];
218
+ withEndpoint(endpointName: any): any;
219
+ endpoint: any;
220
+ withAccess(a: any): any;
221
+ withProperty(property: any): any;
222
+ withDescription(description: any): any;
223
+ description: any;
224
+ removeFeature(feature: any): any;
225
+ setAccess(feature: any, a: any): any;
226
+ } | {
227
+ type: string;
228
+ name: any;
229
+ property: any;
230
+ access: any;
231
+ withEndpoint(endpointName: any): any;
232
+ endpoint: any;
233
+ withAccess(a: any): any;
234
+ withProperty(property: any): any;
235
+ withDescription(description: any): any;
236
+ description: any;
237
+ removeFeature(feature: any): any;
238
+ setAccess(feature: any, a: any): any;
239
+ })[];
189
240
  meta: {
190
241
  multiEndpoint: boolean;
191
242
  tuyaThermostatPreset: {
@@ -3053,25 +3104,8 @@ declare const _exports: ({
3053
3104
  };
3054
3105
  };
3055
3106
  };
3056
- endpoint: (device: any) => {
3057
- l1: number;
3058
- l2: number;
3059
- l3: number;
3060
- l4: number;
3061
- l5: number;
3062
- l6: number;
3063
- l7: number;
3064
- l8: number;
3065
- l9: number;
3066
- l10: number;
3067
- l11: number;
3068
- l12: number;
3069
- l13: number;
3070
- l14: number;
3071
- l15: number;
3072
- l16: number;
3073
- action: number;
3074
- };
3107
+ endpoint: (device: any) => any[];
3108
+ configure: (device: any, coordinatorEndpoint: any, logger: any, options: any) => Promise<void>;
3075
3109
  } | {
3076
3110
  zigbeeModel: string[];
3077
3111
  model: string;
@@ -6,7 +6,7 @@ const extend = require('../lib/extend');
6
6
  const constants = require('../lib/constants');
7
7
  const e = exposes.presets;
8
8
  const ea = exposes.access;
9
- const { calibrateAndPrecisionRoundOptions } = require('../lib/utils');
9
+ const { calibrateAndPrecisionRoundOptions, postfixWithEndpointName } = require('../lib/utils');
10
10
  const tzLocal = {
11
11
  tirouter: {
12
12
  key: ['transmit_power'],
@@ -276,7 +276,128 @@ const fzLocal = {
276
276
  return result;
277
277
  },
278
278
  },
279
+ humidity2: {
280
+ cluster: 'msRelativeHumidity',
281
+ type: ['attributeReport', 'readResponse'],
282
+ options: [exposes.options.precision('humidity'), exposes.options.calibration('humidity')],
283
+ convert: (model, msg, publish, options, meta) => {
284
+ // multi-endpoint version based on the stastard onverter 'fz.humidity'
285
+ const humidity = parseFloat(msg.data['measuredValue']) / 100.0;
286
+ // https://github.com/Koenkk/zigbee2mqtt/issues/798
287
+ // Sometimes the sensor publishes non-realistic vales, it should only publish message
288
+ // in the 0 - 100 range, don't produce messages beyond these values.
289
+ if (humidity >= 0 && humidity <= 100) {
290
+ const multiEndpoint = model.meta && model.meta.hasOwnProperty('multiEndpoint') && model.meta.multiEndpoint;
291
+ const property = (multiEndpoint) ? postfixWithEndpointName('humidity', msg, model, meta) : 'humidity';
292
+ return { [property]: calibrateAndPrecisionRoundOptions(humidity, options, 'humidity') };
293
+ }
294
+ },
295
+ },
296
+ illuminance2: {
297
+ cluster: 'msIlluminanceMeasurement',
298
+ type: ['attributeReport', 'readResponse'],
299
+ options: [exposes.options.calibration('illuminance', 'percentual'), exposes.options.calibration('illuminance_lux', 'percentual')],
300
+ convert: (model, msg, publish, options, meta) => {
301
+ // multi-endpoint version based on the stastard onverter 'fz.illuminance'
302
+ // DEPRECATED: only return lux here (change illuminance_lux -> illuminance)
303
+ const illuminance = msg.data['measuredValue'];
304
+ const illuminanceLux = illuminance === 0 ? 0 : Math.pow(10, (illuminance - 1) / 10000);
305
+ const multiEndpoint = model.meta && model.meta.hasOwnProperty('multiEndpoint') && model.meta.multiEndpoint;
306
+ const property1 = (multiEndpoint) ? postfixWithEndpointName('illuminance', msg, model, meta) : 'illuminance';
307
+ const property2 = (multiEndpoint) ? postfixWithEndpointName('illuminance_lux', msg, model, meta) : 'illuminance_lux';
308
+ return {
309
+ [property1]: calibrateAndPrecisionRoundOptions(illuminance, options, 'illuminance'),
310
+ [property2]: calibrateAndPrecisionRoundOptions(illuminanceLux, options, 'illuminance_lux'),
311
+ };
312
+ },
313
+ },
314
+ pressure2: {
315
+ cluster: 'msPressureMeasurement',
316
+ type: ['attributeReport', 'readResponse'],
317
+ options: [exposes.options.precision('pressure'), exposes.options.calibration('pressure')],
318
+ convert: (model, msg, publish, options, meta) => {
319
+ // multi-endpoint version based on the stastard onverter 'fz.pressure'
320
+ let pressure = 0;
321
+ if (msg.data.hasOwnProperty('scaledValue')) {
322
+ const scale = msg.endpoint.getClusterAttributeValue('msPressureMeasurement', 'scale');
323
+ pressure = msg.data['scaledValue'] / Math.pow(10, scale) / 100.0; // convert to hPa
324
+ }
325
+ else {
326
+ pressure = parseFloat(msg.data['measuredValue']);
327
+ }
328
+ const multiEndpoint = model.meta && model.meta.hasOwnProperty('multiEndpoint') && model.meta.multiEndpoint;
329
+ const property = (multiEndpoint) ? postfixWithEndpointName('pressure', msg, model, meta) : 'pressure';
330
+ return { [property]: calibrateAndPrecisionRoundOptions(pressure, options, 'pressure') };
331
+ },
332
+ },
279
333
  };
334
+ function ptvoGetMetaOption(device, key, defaultValue) {
335
+ if (device != null) {
336
+ const value = device.meta[key];
337
+ if (value === undefined) {
338
+ return defaultValue;
339
+ }
340
+ else {
341
+ return value;
342
+ }
343
+ }
344
+ return defaultValue;
345
+ }
346
+ function ptvoSetMetaOption(device, key, value) {
347
+ if (device != null && key != null) {
348
+ device.meta[key] = value;
349
+ }
350
+ }
351
+ function ptvoAddStandardExposes(endpoint, expose, options) {
352
+ const epId = endpoint.ID;
353
+ const epName = `l${epId}`;
354
+ if (endpoint.supportsInputCluster('lightingColorCtrl')) {
355
+ expose.push(e.light_brightness_colorxy().withEndpoint('l1').withEndpoint(epName));
356
+ options['exposed_onoff'] = true;
357
+ options['exposed_analog'] = true;
358
+ options['exposed_colorcontrol'] = true;
359
+ }
360
+ else if (endpoint.supportsInputCluster('genLevelCtrl')) {
361
+ expose.push(e.light_brightness().withEndpoint(epName));
362
+ options['exposed_onoff'] = true;
363
+ options['exposed_analog'] = true;
364
+ options['exposed_levelcontrol'] = true;
365
+ }
366
+ if (endpoint.supportsInputCluster('genOnOff') || endpoint.supportsOutputCluster('genOnOff')) {
367
+ if (!options['exposed_onoff']) {
368
+ expose.push(e.switch().withEndpoint(epName));
369
+ }
370
+ }
371
+ if (endpoint.supportsInputCluster('genAnalogInput') || endpoint.supportsOutputCluster('genAnalogInput')) {
372
+ if (!options['exposed_analog']) {
373
+ options['exposed_analog'] = true;
374
+ expose.push(exposes.text(epName, ea.ALL).withEndpoint(epName)
375
+ .withProperty(epName).withDescription('State or sensor value'));
376
+ }
377
+ }
378
+ if (endpoint.supportsInputCluster('msTemperatureMeasurement')) {
379
+ expose.push(e.temperature().withEndpoint(epName));
380
+ options['exposed_temperature'] = true;
381
+ }
382
+ if (endpoint.supportsInputCluster('msRelativeHumidity')) {
383
+ expose.push(e.humidity().withEndpoint(epName));
384
+ options['exposed_humidity'] = true;
385
+ }
386
+ if (endpoint.supportsInputCluster('msPressureMeasurement')) {
387
+ expose.push(e.pressure().withEndpoint(epName));
388
+ options['exposed_pressure'] = true;
389
+ }
390
+ if (endpoint.supportsInputCluster('msIlluminanceMeasurement')) {
391
+ expose.push(e.illuminance().withEndpoint(epName));
392
+ options['exposed_illuminance'] = true;
393
+ }
394
+ if (endpoint.supportsInputCluster('genPowerCfg')) {
395
+ options['expose_battery'] = true;
396
+ }
397
+ if (endpoint.supportsInputCluster('genMultistateInput') || endpoint.supportsOutputCluster('genMultistateInput')) {
398
+ options['expose_action'] = true;
399
+ }
400
+ }
280
401
  module.exports = [
281
402
  {
282
403
  zigbeeModel: ['ti.router'],
@@ -329,25 +450,116 @@ module.exports = [
329
450
  vendor: 'Custom devices (DiY)',
330
451
  description: '[Multi-channel relay switch](https://ptvo.info/zigbee-switch-configurable-firmware-router-199/)',
331
452
  fromZigbee: [fz.on_off, fz.ptvo_multistate_action, fz.legacy.ptvo_switch_buttons, fz.ptvo_switch_uart,
332
- fz.ptvo_switch_analog_input, fz.brightness, fz.ignore_basic_report],
453
+ fz.ptvo_switch_analog_input, fz.brightness, fz.ignore_basic_report, fz.temperature,
454
+ fzLocal.humidity2, fzLocal.pressure2, fzLocal.illuminance2],
333
455
  toZigbee: [tz.ptvo_switch_trigger, tz.ptvo_switch_uart, tz.ptvo_switch_analog_input, tz.ptvo_switch_light_brightness, tz.on_off],
334
- exposes: [exposes.text('action').withAccess(ea.STATE_SET)].concat(((enpoinsCount) => {
335
- const features = [];
336
- for (let i = 1; i <= enpoinsCount; i++) {
337
- const epName = `l${i}`;
338
- features.push(e.switch().withEndpoint(epName));
339
- features.push(exposes.text(epName, ea.ALL).withEndpoint(epName)
340
- .withProperty(epName).withDescription('State or sensor value'));
341
- }
342
- return features;
343
- })(16)),
456
+ exposes: (device, options) => {
457
+ const expose = [];
458
+ const exposeOptions = {};
459
+ const deviceConfig = ptvoGetMetaOption(device, 'device_config', '');
460
+ if (deviceConfig === '') {
461
+ if ((device != null) && device.endpoints) {
462
+ for (const endpoint of device.endpoints) {
463
+ ptvoAddStandardExposes(endpoint, expose, exposeOptions);
464
+ }
465
+ }
466
+ else {
467
+ // fallback code
468
+ for (let epId = 1; epId <= 8; epId++) {
469
+ const epName = `l${epId}`;
470
+ expose.push(exposes.text(epName, ea.ALL).withEndpoint(epName)
471
+ .withProperty(epName).withDescription('State or sensor value'));
472
+ expose.push(e.switch().withEndpoint(epName));
473
+ }
474
+ }
475
+ }
476
+ else {
477
+ for (let i = 0; i < deviceConfig.length; i++) {
478
+ const epConfig = deviceConfig.charCodeAt(i);
479
+ if (epConfig <= 0x20) {
480
+ continue;
481
+ }
482
+ const epId = i + 1;
483
+ const epName = `l${epId}`;
484
+ if ((epConfig & 0x01) != 0) {
485
+ // GPIO input
486
+ exposeOptions['expose_action'] = true;
487
+ }
488
+ if ((epConfig & 0x02) != 0) {
489
+ // GPIO output
490
+ exposeOptions['exposed_onoff'] = true;
491
+ expose.push(e.switch().withEndpoint(epName));
492
+ }
493
+ if ((epConfig & 0x04) != 0) {
494
+ // reportable analog value
495
+ exposeOptions['exposed_analog'] = true;
496
+ expose.push(exposes.numeric(epName, ea.STATE).withDescription('State or sensor value'));
497
+ }
498
+ else if ((epConfig & 0x08) != 0) {
499
+ // readable analog value
500
+ exposeOptions['exposed_analog'] = true;
501
+ expose.push(exposes.numeric(epName, ea.STATE_SET)
502
+ .withValueMin(-9999999).withValueMax(9999999).withValueStep(1)
503
+ .withDescription('State or sensor value'));
504
+ }
505
+ const endpoint = device.getEndpoint(epId);
506
+ if (!endpoint) {
507
+ continue;
508
+ }
509
+ ptvoAddStandardExposes(endpoint, expose, exposeOptions);
510
+ }
511
+ }
512
+ if (exposeOptions['expose_action']) {
513
+ expose.push(e.action(['single', 'double', 'triple', 'hold', 'release']));
514
+ }
515
+ if (exposeOptions['expose_battery']) {
516
+ expose.push(e.battery());
517
+ }
518
+ expose.push(e.linkquality());
519
+ return expose;
520
+ },
344
521
  meta: { multiEndpoint: true, tuyaThermostatPreset: fz.legacy /* for subclassed custom converters */ },
345
522
  endpoint: (device) => {
346
- return {
347
- l1: 1, l2: 2, l3: 3, l4: 4, l5: 5, l6: 6, l7: 7, l8: 8,
348
- l9: 9, l10: 10, l11: 11, l12: 12, l13: 13, l14: 14, l15: 15, l16: 16,
349
- action: 1,
350
- };
523
+ const endpointList = [];
524
+ const deviceConfig = ptvoGetMetaOption(device, 'device_config', '');
525
+ if (deviceConfig === '') {
526
+ if ((device != null) && device.endpoints) {
527
+ for (const endpoint of device.endpoints) {
528
+ const epId = endpoint.ID;
529
+ const epName = `l${epId}`;
530
+ endpointList[epName] = epId;
531
+ }
532
+ }
533
+ else {
534
+ // fallback code
535
+ for (let epId = 1; epId <= 8; epId++) {
536
+ const epName = `l${epId}`;
537
+ endpointList[epName] = epId;
538
+ }
539
+ }
540
+ }
541
+ else {
542
+ for (let i = 0; i < deviceConfig.length; i++) {
543
+ const epConfig = deviceConfig.charCodeAt(i);
544
+ if (epConfig === 0x20) {
545
+ continue;
546
+ }
547
+ const epId = i + 1;
548
+ const epName = `l${epId}`;
549
+ endpointList[epName] = epId;
550
+ }
551
+ }
552
+ endpointList['action'] = 1;
553
+ return endpointList;
554
+ },
555
+ configure: async (device, coordinatorEndpoint, logger, options) => {
556
+ if (device != null) {
557
+ const controlEp = device.getEndpoint(1);
558
+ if (controlEp != null) {
559
+ ptvoSetMetaOption(device, 'device_config', (await controlEp.read('genBasic', ['locationDesc'])).locationDesc);
560
+ device.save();
561
+ }
562
+ }
351
563
  },
352
564
  },
353
565
  {