zigbee-herdsman-converters 25.103.0 → 25.105.0

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.
Files changed (54) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/dist/converters/actions.d.ts.map +1 -1
  3. package/dist/converters/actions.js +0 -2
  4. package/dist/converters/actions.js.map +1 -1
  5. package/dist/devices/efekta.d.ts.map +1 -1
  6. package/dist/devices/efekta.js +33 -13
  7. package/dist/devices/efekta.js.map +1 -1
  8. package/dist/devices/envilar.js +1 -1
  9. package/dist/devices/heiman.d.ts.map +1 -1
  10. package/dist/devices/heiman.js +2 -1
  11. package/dist/devices/heiman.js.map +1 -1
  12. package/dist/devices/istar.d.ts.map +1 -1
  13. package/dist/devices/istar.js +7 -0
  14. package/dist/devices/istar.js.map +1 -1
  15. package/dist/devices/lincukoo.d.ts.map +1 -1
  16. package/dist/devices/lincukoo.js +351 -7
  17. package/dist/devices/lincukoo.js.map +1 -1
  18. package/dist/devices/lumi.d.ts.map +1 -1
  19. package/dist/devices/lumi.js +1 -0
  20. package/dist/devices/lumi.js.map +1 -1
  21. package/dist/devices/moes.js +1 -1
  22. package/dist/devices/moes.js.map +1 -1
  23. package/dist/devices/namron.d.ts.map +1 -1
  24. package/dist/devices/namron.js +9 -0
  25. package/dist/devices/namron.js.map +1 -1
  26. package/dist/devices/niko.d.ts.map +1 -1
  27. package/dist/devices/niko.js +8 -4
  28. package/dist/devices/niko.js.map +1 -1
  29. package/dist/devices/nous.js +2 -2
  30. package/dist/devices/nous.js.map +1 -1
  31. package/dist/devices/philips.js +1 -1
  32. package/dist/devices/philips.js.map +1 -1
  33. package/dist/devices/shelly.d.ts.map +1 -1
  34. package/dist/devices/shelly.js +21 -0
  35. package/dist/devices/shelly.js.map +1 -1
  36. package/dist/devices/sinope.js +2 -2
  37. package/dist/devices/sinope.js.map +1 -1
  38. package/dist/devices/sonoff.d.ts.map +1 -1
  39. package/dist/devices/sonoff.js +88 -34
  40. package/dist/devices/sonoff.js.map +1 -1
  41. package/dist/devices/sunricher.d.ts.map +1 -1
  42. package/dist/devices/sunricher.js +38 -84
  43. package/dist/devices/sunricher.js.map +1 -1
  44. package/dist/devices/tuya.d.ts.map +1 -1
  45. package/dist/devices/tuya.js +214 -92
  46. package/dist/devices/tuya.js.map +1 -1
  47. package/dist/lib/ikea.d.ts.map +1 -1
  48. package/dist/lib/ikea.js +6 -13
  49. package/dist/lib/ikea.js.map +1 -1
  50. package/dist/lib/ubisys.d.ts.map +1 -1
  51. package/dist/lib/ubisys.js +2 -2
  52. package/dist/lib/ubisys.js.map +1 -1
  53. package/dist/models-index.json +1 -1
  54. package/package.json +1 -1
@@ -951,7 +951,7 @@ const fzLocal = {
951
951
  TS0201_humidity: {
952
952
  ...fz.humidity,
953
953
  convert: (model, msg, publish, options, meta) => {
954
- if (["_TZ3210_ncw88jfq", "_TZ3000_ywagc4rj", "_TZ3000_isw9u95y", "_TZ3000_yupc0pb7"].includes(meta.device.manufacturerName)) {
954
+ if (["_TZ3210_ncw88jfq", "_TZ3000_ywagc4rj", "_TZ3000_isw9u95y"].includes(meta.device.manufacturerName)) {
955
955
  msg.data.measuredValue *= 10;
956
956
  }
957
957
  return fz.humidity.convert(model, msg, publish, options, meta);
@@ -3345,6 +3345,7 @@ exports.definitions = [
3345
3345
  "_TZ3000_shopg9ss",
3346
3346
  "_TZ3000_n0lphcok",
3347
3347
  "_TZ3000_r80pzsb9",
3348
+ "_TZ3000_sgpbz53b",
3348
3349
  ]),
3349
3350
  ...tuya.fingerprint("TS0001", ["_TZ3000_n0lphcok", "_TZ3000_wn65ixz9", "_TZ3000_trdx8uxs", "_TZ3000_gdsvhfao"]),
3350
3351
  ],
@@ -4310,6 +4311,33 @@ exports.definitions = [
4310
4311
  ],
4311
4312
  },
4312
4313
  },
4314
+ {
4315
+ fingerprint: tuya.fingerprint("TS0301", ["_TZE210_inpjmc0h"]),
4316
+ model: "TS0301_dual_rail",
4317
+ vendor: "Tuya",
4318
+ description: "Top-down bottom-up dual motor shade",
4319
+ extend: [tuya.modernExtend.tuyaBase({ dp: true }), m.deviceEndpoints({ endpoints: { bottom: 1, top: 1 } })],
4320
+ exposes: [
4321
+ e.cover_position().withEndpoint("bottom").withDescription("Bottom rail"),
4322
+ e.cover_position().withEndpoint("top").withDescription("Top rail"),
4323
+ e.battery(),
4324
+ ],
4325
+ meta: {
4326
+ tuyaDatapoints: [
4327
+ // Bottom rail - DP109 is control, DP101 is set position, DP102 is current position
4328
+ [109, "state_bottom", tuya.valueConverterBasic.lookup({ OPEN: tuya.enum(0), CLOSE: tuya.enum(1), STOP: tuya.enum(2) })],
4329
+ [101, "position_bottom", tuya.valueConverter.coverPositionInverted],
4330
+ [102, "position_bottom", tuya.valueConverter.coverPositionInverted],
4331
+ // Top rail - DP1 is control, DP2 is set position, DP3 is current position
4332
+ // Note: State commands are reversed for Home Assistant compatibility with top-down operation
4333
+ [1, "state_top", tuya.valueConverterBasic.lookup({ OPEN: tuya.enum(1), CLOSE: tuya.enum(0), STOP: tuya.enum(2) })],
4334
+ [2, "position_top", tuya.valueConverter.coverPosition],
4335
+ [3, "position_top", tuya.valueConverter.coverPosition],
4336
+ // Battery
4337
+ [13, "battery", tuya.valueConverter.raw],
4338
+ ],
4339
+ },
4340
+ },
4313
4341
  {
4314
4342
  fingerprint: tuya.fingerprint("TS0601", [
4315
4343
  "_TZE200_aqnazj70",
@@ -5209,24 +5237,30 @@ exports.definitions = [
5209
5237
  },
5210
5238
  {
5211
5239
  fingerprint: tuya.fingerprint("TS0601", ["_TZE200_dzuqwsyg", "_TZE204_dzuqwsyg"]),
5212
- model: "BAC-003",
5240
+ model: "BAC-002-ALZB",
5213
5241
  vendor: "Tuya",
5214
5242
  description: "FCU thermostat temperature controller",
5215
5243
  extend: [tuya.modernExtend.tuyaBase({ dp: true, forceTimeUpdates: true, timeStart: "1970" })],
5216
5244
  options: [
5217
5245
  e
5218
- .enum("control_sequence_of_operation", ea.SET, ["cooling_only", "cooling_and_heating_4-pipes"])
5219
- .withDescription("Operating environment of the thermostat"),
5220
- e.binary("expose_device_state", ea.SET, true, false).withDescription("Expose device power state as a separate property when enabled."),
5246
+ .enum("control_sequence_of_operation", ea.SET, ["cooling_only", "cooling_and_heating"])
5247
+ .withLabel("Device Configuration")
5248
+ .withDescription("Report either cooling and fan or cooling, heating and fan capability."),
5249
+ e
5250
+ .binary("expose_device_state", ea.SET, true, false)
5251
+ .withLabel("Expose device switch")
5252
+ .withDescription("Expose a separate on/off switch, instead of including it in system mode."),
5253
+ e
5254
+ .binary("wake_before_power_transition", ea.SET, true, false)
5255
+ .withLabel("Wake before Power Transition")
5256
+ .withDescription("Send a wake-up command before turning the device on or/off, required for some firmware revisions."),
5221
5257
  ],
5222
5258
  exposes: (device, options) => {
5223
5259
  const system_modes = ["off", "cool", "heat", "fan_only"];
5224
- // Device can operate either in 2-pipe or 4-pipe configuration
5225
- // For 2-pipe configurations remove 'heat' mode
5226
- switch (options?.control_sequence_of_operation) {
5227
- case "cooling_only":
5228
- system_modes.splice(2, 1);
5229
- break;
5260
+ // Device can operate either in cooling or heating/cooling configuration
5261
+ // For cooling only configurations remove 'heat' mode
5262
+ if (options.control_sequence_of_operation === "cooling_only") {
5263
+ system_modes.splice(2, 1);
5230
5264
  }
5231
5265
  const exposes = [
5232
5266
  e
@@ -5236,25 +5270,19 @@ exports.definitions = [
5236
5270
  .withFanMode(["low", "medium", "high", "auto"], ea.STATE_SET)
5237
5271
  .withSetpoint("current_heating_setpoint", 5, 35, 1, ea.STATE_SET)
5238
5272
  .withPreset(["auto", "manual"])
5239
- .withLocalTemperatureCalibration(-3, 3, 1, ea.STATE_SET),
5273
+ .withLocalTemperatureCalibration(-9, 9, 1, ea.STATE_SET),
5240
5274
  e.child_lock(),
5241
- e
5242
- .composite("schedule", "schedule", ea.STATE_SET)
5243
- .withFeature(e.text("weekdays", ea.SET).withDescription('Schedule (1-5), 4 periods in format "hh:mm/tt".'))
5244
- .withFeature(e.text("saturday", ea.SET).withDescription('Schedule (6), 4 periods in format "hh:mm/tt".'))
5245
- .withFeature(e.text("sunday", ea.SET).withDescription('Schedule (7), 4 periods in format "hh:mm/tt".'))
5246
- .withDescription('Auto-mode schedule, 4 periods each per category. Example: "06:00/20 11:30/21 13:30/22 17:30/23.5".'),
5247
- e.max_temperature().withValueMin(35).withValueMax(45).withPreset("default", 35, "Default value"),
5248
- e
5249
- .numeric("deadzone_temperature", ea.STATE_SET)
5250
- .withUnit("°C")
5251
- .withValueMax(5)
5252
- .withValueMin(1)
5253
- .withValueStep(1)
5254
- .withPreset("default", 1, "Default value")
5255
- .withDescription("The delta between local_temperature and current_heating_setpoint to trigger activity"),
5275
+ e.max_temperature().withValueMin(35).withValueMax(45),
5276
+ e.numeric("deadzone_temperature", ea.STATE_SET).withUnit("°C").withValueMax(5).withValueMin(0),
5277
+ e.text("schedule_text", ea.STATE_SET).withDescription(`Weekly schedule in the format "HH:MM/TT HH:MM/TT ...".
5278
+ Example for 12 segments:
5279
+ "06:00/20 11:30/21 13:30/22 17:30/23 06:00/24 12:00/23 14:30/22 17:30/21 06:00/19 12:30/20 14:30/21 18:30/20".
5280
+ Each segment contains:
5281
+ - HH:MM: Time in 24-hour format.
5282
+ - TT: Temperature in °C.
5283
+ Ensure all 12 segments are defined and separated by spaces.`),
5256
5284
  ];
5257
- if (options?.expose_device_state === true) {
5285
+ if (options.expose_device_state === true) {
5258
5286
  exposes.unshift(e.binary("state", ea.STATE_SET, "ON", "OFF").withDescription("Turn the thermostat ON or OFF"));
5259
5287
  }
5260
5288
  return exposes;
@@ -5267,15 +5295,14 @@ exports.definitions = [
5267
5295
  "state",
5268
5296
  {
5269
5297
  to: async (v, meta) => {
5270
- if (meta.options?.expose_device_state === true) {
5298
+ if (meta.options.expose_device_state === true) {
5271
5299
  await tuya.sendDataPointBool(meta.device.endpoints[0], 1, utils.getFromLookup(v, { on: true, off: false }), "dataRequest", 1);
5272
5300
  }
5273
5301
  },
5274
5302
  from: (v, meta, options) => {
5275
5303
  meta.state.system_mode = v === true ? (meta.state.system_mode_device ?? "cool") : "off";
5276
- if (options?.expose_device_state === true) {
5304
+ if (options.expose_device_state === true)
5277
5305
  return v === true ? "ON" : "OFF";
5278
- }
5279
5306
  delete meta.state.state;
5280
5307
  },
5281
5308
  },
@@ -5284,22 +5311,29 @@ exports.definitions = [
5284
5311
  2,
5285
5312
  "system_mode",
5286
5313
  {
5287
- // Extend system_mode to support 'off' in addition to 'cool', 'heat' and 'fan_only'
5288
5314
  to: async (v, meta) => {
5289
- const entity = meta.device.endpoints[0];
5290
- // Power State
5291
- await tuya.sendDataPointBool(entity, 1, v !== "off", "dataRequest", 1);
5292
- switch (v) {
5293
- case "cool":
5294
- await tuya.sendDataPointEnum(entity, 2, 0, "dataRequest", 1);
5295
- break;
5296
- case "heat":
5297
- await tuya.sendDataPointEnum(entity, 2, 1, "dataRequest", 1);
5298
- break;
5299
- case "fan_only":
5300
- await tuya.sendDataPointEnum(entity, 2, 2, "dataRequest", 1);
5301
- break;
5315
+ const ep = meta.device.endpoints[0];
5316
+ if (v === "off") {
5317
+ if (meta.options.wake_before_power_transition === true) {
5318
+ await tuya.sendDataPointBool(ep, 1, true, "dataRequest", 1);
5319
+ await new Promise((r) => setTimeout(r, 120));
5320
+ }
5321
+ await tuya.sendDataPointBool(ep, 1, false, "dataRequest", 1);
5322
+ return;
5302
5323
  }
5324
+ if (meta.options.wake_before_power_transition === true) {
5325
+ if (meta.state.system_mode === "off") {
5326
+ await tuya.sendDataPointBool(ep, 1, true, "dataRequest", 1);
5327
+ await new Promise((r) => setTimeout(r, 120));
5328
+ }
5329
+ }
5330
+ await tuya.sendDataPointBool(ep, 1, true, "dataRequest", 1);
5331
+ if (v === "cool")
5332
+ await tuya.sendDataPointEnum(ep, 2, 0, "dataRequest", 1);
5333
+ if (v === "heat")
5334
+ await tuya.sendDataPointEnum(ep, 2, 1, "dataRequest", 1);
5335
+ if (v === "fan_only")
5336
+ await tuya.sendDataPointEnum(ep, 2, 2, "dataRequest", 1);
5303
5337
  },
5304
5338
  from: (v, meta) => {
5305
5339
  const modes = ["cool", "heat", "fan_only"];
@@ -5308,7 +5342,21 @@ exports.definitions = [
5308
5342
  },
5309
5343
  },
5310
5344
  ],
5311
- [4, "preset", tuya.valueConverterBasic.lookup({ manual: true, auto: false })],
5345
+ [
5346
+ 4,
5347
+ "preset",
5348
+ {
5349
+ to: async (v, meta) => {
5350
+ const ep = meta.device.endpoints[0];
5351
+ await tuya.sendDataPointBool(ep, 4, v === "manual");
5352
+ },
5353
+ from: (v, meta) => {
5354
+ const preset = v ? "manual" : "auto";
5355
+ meta.state.preset = preset;
5356
+ return preset;
5357
+ },
5358
+ },
5359
+ ],
5312
5360
  [16, "current_heating_setpoint", tuya.valueConverter.raw],
5313
5361
  [19, "max_temperature", tuya.valueConverter.raw],
5314
5362
  [24, "local_temperature", tuya.valueConverter.divideBy10],
@@ -5329,52 +5377,45 @@ exports.definitions = [
5329
5377
  101,
5330
5378
  "schedule",
5331
5379
  {
5332
- to: (v, meta) => {
5333
- const periods = (value) => {
5334
- const regex = /((?<h>[01][0-9]|2[0-3]):(?<m>[0-5][0-9])\/(?<t>[0-3][0-9](\.[0,5]|)))/gm;
5335
- const matches = [...value.matchAll(regex)];
5336
- if (matches.length === 4) {
5337
- return matches.reduce((arr, m) => {
5338
- arr.push(Number.parseInt(m.groups.h, 10));
5339
- arr.push(Number.parseInt(m.groups.m, 10));
5340
- arr.push(Number.parseFloat(m.groups.t) * 2);
5341
- return arr;
5342
- }, []);
5343
- }
5344
- logger_1.logger.warning("Ignoring invalid or incomplete schedule", NS);
5345
- };
5346
- const schedule = [...periods(v.weekdays), ...periods(v.saturday), ...periods(v.sunday)];
5347
- return schedule;
5348
- },
5349
5380
  from: (v, meta) => {
5350
- const format = (data) => {
5351
- return data.reduce((a, v, i) => {
5352
- switch (i % 3) {
5353
- // Hour
5354
- case 0:
5355
- return `${a}${i > 0 ? " " : ""}${v.toString().padStart(2, "0")}`;
5356
- // Minute
5357
- case 1:
5358
- return `${a}:${v.toString().padStart(2, "0")}`;
5359
- // Setpoint
5360
- case 2:
5361
- return `${a}/${v / 2}`;
5362
- default:
5363
- throw new Error(`Unexpected index ${i} in schedule data`);
5364
- }
5365
- }, "");
5366
- };
5367
- return {
5368
- weekdays: format(v.slice(0, 12)),
5369
- saturday: format(v.slice(1 * 12, 2 * 12)),
5370
- sunday: format(v.slice(2 * 12, 3 * 12)),
5371
- };
5381
+ const format = (data) => data.reduce((txt, val, i) => {
5382
+ if (i % 3 === 0)
5383
+ return `${txt}${i > 0 ? " " : ""}${val.toString().padStart(2, "0")}`;
5384
+ if (i % 3 === 1)
5385
+ return `${txt}:${val.toString().padStart(2, "0")}`;
5386
+ return `${txt}/${val / 2}`;
5387
+ }, "");
5388
+ const weekdays = format(v.slice(0, 12));
5389
+ const saturday = format(v.slice(12, 24));
5390
+ const sunday = format(v.slice(24, 36));
5391
+ const full = `${weekdays} ${saturday} ${sunday}`.trim();
5392
+ meta.state.schedule_text = full;
5393
+ return full;
5394
+ },
5395
+ },
5396
+ ],
5397
+ [
5398
+ 202,
5399
+ "schedule_text",
5400
+ {
5401
+ to: async (v, meta) => {
5402
+ const regex = /((?<h>[01][0-9]|2[0-3]):(?<m>[0-5][0-9])\/(?<t>[0-3]?[0-9](\.[0,5])?))/gm;
5403
+ const matches = [...v.matchAll(regex)];
5404
+ if (matches.length !== 12)
5405
+ return;
5406
+ const result = [];
5407
+ for (const m of matches) {
5408
+ result.push(Number(m.groups?.h));
5409
+ result.push(Number(m.groups?.m));
5410
+ result.push(Number(m.groups?.t) * 2);
5411
+ }
5412
+ await tuya.sendDataPointRaw(meta.device.endpoints[0], 101, Buffer.from(result));
5372
5413
  },
5373
5414
  },
5374
5415
  ],
5375
5416
  ],
5376
5417
  },
5377
- whiteLabel: [tuya.whitelabel("Tuya", "BAC-002-ALZB", "FCU thermostat temperature controller", ["_TZE200_dzuqwsyg"])],
5418
+ whiteLabel: [tuya.whitelabel("Tuya", "BAC-003", "FCU thermostat temperature controller", ["_TZE204_dzuqwsyg"])],
5378
5419
  },
5379
5420
  {
5380
5421
  fingerprint: tuya.fingerprint("TS0601", ["_TZE200_qq9mpfhw"]),
@@ -5855,7 +5896,14 @@ exports.definitions = [
5855
5896
  },
5856
5897
  },
5857
5898
  {
5858
- fingerprint: tuya.fingerprint("TS0003", ["_TZ3000_4o16jdca", "_TZ3000_odzoiovu", "_TZ3000_hbic3ka3", "_TZ3000_lvhy15ix", "_TZ3000_mhhxxjrs"]),
5899
+ fingerprint: tuya.fingerprint("TS0003", [
5900
+ "_TZ3000_4o16jdca",
5901
+ "_TZ3000_odzoiovu",
5902
+ "_TZ3000_hbic3ka3",
5903
+ "_TZ3000_lvhy15ix",
5904
+ "_TZ3000_mhhxxjrs",
5905
+ "_TZ3000_iv4eq7eh",
5906
+ ]),
5859
5907
  model: "TS0003_switch_module_2",
5860
5908
  vendor: "Tuya",
5861
5909
  description: "3 gang switch module",
@@ -5878,7 +5926,7 @@ exports.definitions = [
5878
5926
  await reporting.bind(device.getEndpoint(3), coordinatorEndpoint, ["genOnOff"]);
5879
5927
  },
5880
5928
  whiteLabel: [
5881
- tuya.whitelabel("AVATTO", "ZWSM16-3-Zigbee", "3 gang switch module", ["_TZ3000_hbic3ka3"]),
5929
+ tuya.whitelabel("AVATTO", "ZWSM16-3-Zigbee", "3 gang switch module", ["_TZ3000_hbic3ka3", "_TZ3000_iv4eq7eh"]),
5882
5930
  tuya.whitelabel("iHseno", "_TZ3000_mhhxxjrs", "3 gang switch module", ["_TZ3000_mhhxxjrs"]),
5883
5931
  ],
5884
5932
  },
@@ -5994,8 +6042,8 @@ exports.definitions = [
5994
6042
  switchType: true,
5995
6043
  onOffCountdown: (m) => m !== "_TZ3000_xfxpoxe0",
5996
6044
  indicatorMode: (m) => m !== "_TZ3000_xfxpoxe0",
5997
- powerOutageMemory: (m) => m === "_TZ3000_xfxpoxe0",
5998
- backlightModeOffOn: (m) => m === "_TZ3000_xfxpoxe0",
6045
+ powerOutageMemory: true,
6046
+ backlightModeOffOn: true,
5999
6047
  }),
6000
6048
  ],
6001
6049
  configure: async (device, coordinatorEndpoint) => {
@@ -10903,7 +10951,7 @@ exports.definitions = [
10903
10951
  ],
10904
10952
  },
10905
10953
  {
10906
- fingerprint: tuya.fingerprint("TS0601", ["_TZE200_lu01t0zl", "_TZE200_vrfecyku", "_TZE200_ypprdwsl", "_TZE200_jkbljri7"]),
10954
+ fingerprint: tuya.fingerprint("TS0601", ["_TZE200_lu01t0zl", "_TZE200_vrfecyku", "_TZE200_ypprdwsl", "_TZE200_jkbljri7", "_TZE204_bvfld3xc"]),
10907
10955
  model: "MIR-HE200-TY",
10908
10956
  vendor: "Tuya",
10909
10957
  description: "Human presence sensor with fall function",
@@ -14739,6 +14787,53 @@ exports.definitions = [
14739
14787
  ],
14740
14788
  },
14741
14789
  },
14790
+ {
14791
+ fingerprint: tuya.fingerprint("TS0601", ["_TZE284_yrwmnya3"]),
14792
+ model: "M9-zigbee-SL-2",
14793
+ vendor: "Tuya",
14794
+ description: "Smart switch (4 gang + 4 scene) with neutral wire and motion sensing",
14795
+ extend: [tuya.modernExtend.tuyaBase({ dp: true }), m.deviceEndpoints({ endpoints: { l1: 1, l2: 1, l3: 1, l4: 1, state: 1, backlight: 1 } })],
14796
+ fromZigbee: [fz.ias_occupancy_only_alarm_2, tuya.fz.indicator_mode],
14797
+ toZigbee: [tuya.tz.power_on_behavior_1, tuya.tz.backlight_indicator_mode_1],
14798
+ exposes: [
14799
+ ...[1, 2, 3, 4].map((i) => tuya.exposes.switch().withEndpoint(`l${i}`)),
14800
+ ...[0, 1, 2, 3, 4].map((i) => e.power_on_behavior().withAccess(ea.STATE_SET).withEndpoint(`l${i}`)),
14801
+ ...[1, 2, 3, 4].map((i) => tuya.exposes.switchMode().withEndpoint(`l${i}`)),
14802
+ tuya.exposes.backlightModeOffOn().withAccess(ea.STATE_SET),
14803
+ e.action(["scene_0", "scene_1", "scene_2", "scene_3", "scene_4", "scene_5", "scene_6", "scene_7", "scene_8"]),
14804
+ e.presence(),
14805
+ e.numeric("delay", ea.STATE_SET).withUnit("s").withDescription("Light off delay").withValueMin(0).withValueMax(1000),
14806
+ ],
14807
+ meta: {
14808
+ tuyaDatapoints: [
14809
+ [1, "action", tuya.valueConverter.static("scene_1")],
14810
+ [2, "action", tuya.valueConverter.static("scene_2")],
14811
+ [3, "action", tuya.valueConverter.static("scene_3")],
14812
+ [4, "action", tuya.valueConverter.static("scene_4")],
14813
+ [5, "action", tuya.valueConverter.static("scene_5")],
14814
+ [6, "action", tuya.valueConverter.static("scene_6")],
14815
+ [7, "action", tuya.valueConverter.static("scene_7")],
14816
+ [8, "action", tuya.valueConverter.static("scene_8")],
14817
+ [17, "action", tuya.valueConverter.static("scene_0")],
14818
+ [18, "switch_mode_l1", tuya.valueConverter.switchMode],
14819
+ [19, "switch_mode_l2", tuya.valueConverter.switchMode],
14820
+ [20, "switch_mode_l3", tuya.valueConverter.switchMode],
14821
+ [21, "switch_mode_l4", tuya.valueConverter.switchMode],
14822
+ [24, "state_l1", tuya.valueConverter.onOff],
14823
+ [25, "state_l2", tuya.valueConverter.onOff],
14824
+ [26, "state_l3", tuya.valueConverter.onOff],
14825
+ [27, "state_l4", tuya.valueConverter.onOff],
14826
+ [36, "backlight_mode", tuya.valueConverter.onOff],
14827
+ [38, "power_on_behavior_l0", tuya.valueConverter.powerOnBehaviorEnum],
14828
+ [39, "power_on_behavior_l1", tuya.valueConverter.powerOnBehaviorEnum],
14829
+ [40, "power_on_behavior_l2", tuya.valueConverter.powerOnBehaviorEnum],
14830
+ [41, "power_on_behavior_l3", tuya.valueConverter.powerOnBehaviorEnum],
14831
+ [42, "power_on_behavior_l4", tuya.valueConverter.powerOnBehaviorEnum],
14832
+ [101, "presence", tuya.valueConverter.raw],
14833
+ [102, "delay", tuya.valueConverter.raw],
14834
+ ],
14835
+ },
14836
+ },
14742
14837
  {
14743
14838
  fingerprint: tuya.fingerprint("TS0601", ["_TZE204_sooucan5", "_TZE204_oqtpvx51"]),
14744
14839
  model: "YXZBRB58",
@@ -15987,7 +16082,10 @@ exports.definitions = [
15987
16082
  model: "BLE-YL01",
15988
16083
  vendor: "Tuya",
15989
16084
  description: "Smart WiFi Zigbee chlorine meter",
15990
- whiteLabel: [tuya.whitelabel("Tuya", "YK-S03", "Smart pH and Chlorine Tester for Swimming Pool", ["_TZE200_d9mzkhoq"])],
16085
+ whiteLabel: [
16086
+ tuya.whitelabel("Tuya", "YK-S03", "Smart pH and Chlorine Tester for Swimming Pool", ["_TZE200_d9mzkhoq"]),
16087
+ { model: "YY-1099L", vendor: "Tuya" },
16088
+ ],
15991
16089
  // Don't query too often. Values are not always updated. https://github.com/Koenkk/zigbee2mqtt/issues/18704
15992
16090
  extend: [
15993
16091
  tuya.modernExtend.tuyaBase({
@@ -22735,6 +22833,30 @@ exports.definitions = [
22735
22833
  ],
22736
22834
  },
22737
22835
  },
22836
+ {
22837
+ fingerprint: tuya.fingerprint("TS0601", ["_TZE200_fphxkxue"]),
22838
+ model: "ZVL-PRO",
22839
+ vendor: "Nova Digital",
22840
+ description: "Water walve",
22841
+ extend: [tuya.modernExtend.tuyaBase({ dp: true })],
22842
+ exposes: [
22843
+ e.switch().setAccess("state", ea.STATE_SET),
22844
+ e.battery(),
22845
+ e.numeric("countdown", ea.STATE_SET).withUnit("s").withValueMin(0).withValueMax(86400).withDescription("Countdown time in seconds"),
22846
+ e.enum("work_state", ea.STATE, ["auto", "manual", "idle"]).withDescription("Current state of operation"),
22847
+ e.numeric("water_once", ea.STATE).withUnit("L").withDescription("Consumption from the last watering"),
22848
+ ],
22849
+ meta: {
22850
+ tuyaDatapoints: [
22851
+ [1, "state", tuya.valueConverter.onOff],
22852
+ [7, "battery", tuya.valueConverter.raw],
22853
+ [11, "countdown", tuya.valueConverter.raw],
22854
+ [12, "work_state", tuya.valueConverter.raw],
22855
+ [5, "water_once", tuya.valueConverter.raw],
22856
+ [4, "fault", tuya.valueConverter.raw],
22857
+ ],
22858
+ },
22859
+ },
22738
22860
  {
22739
22861
  fingerprint: tuya.fingerprint("TS0601", ["_TZE204_wc2w9t1s"]),
22740
22862
  model: "BOT-R9V-ZB",