zigbee-herdsman-converters 14.0.311 → 14.0.316

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.
@@ -648,6 +648,26 @@ const converters = {
648
648
  return payload;
649
649
  },
650
650
  },
651
+ develco_metering: {
652
+ cluster: 'seMetering',
653
+ type: ['attributeReport', 'readResponse'],
654
+ convert: (model, msg, publish, options, meta) => {
655
+ const result = {};
656
+ if (msg.data.hasOwnProperty('develcoPulseConfiguration')) {
657
+ result[postfixWithEndpointName('pulse_configuration', msg, model)] =
658
+ msg.data['develcoPulseConfiguration'];
659
+ }
660
+
661
+ if (msg.data.hasOwnProperty('develcoInterfaceMode')) {
662
+ result[postfixWithEndpointName('interface_mode', msg, model)] =
663
+ constants.develcoInterfaceMode.hasOwnProperty(msg.data['develcoInterfaceMode']) ?
664
+ constants.develcoInterfaceMode[msg.data['develcoInterfaceMode']] :
665
+ msg.data['develcoInterfaceMode'];
666
+ }
667
+
668
+ return result;
669
+ },
670
+ },
651
671
  electrical_measurement: {
652
672
  /**
653
673
  * When using this converter also add the following to the configure method of the device:
@@ -2485,6 +2505,22 @@ const converters = {
2485
2505
  }
2486
2506
  },
2487
2507
  },
2508
+ livolo_curtain_switch_state: {
2509
+ cluster: 'genPowerCfg',
2510
+ type: ['raw'],
2511
+ convert: (model, msg, publish, options, meta) => {
2512
+ const stateHeader = Buffer.from([122, 209]);
2513
+ if (msg.data.indexOf(stateHeader) === 0) {
2514
+ if (msg.data[10] === 5) {
2515
+ const status = msg.data[14];
2516
+ return {
2517
+ state_left: status === 1 ? 'ON' : 'OFF',
2518
+ state_right: status === 0 ? 'ON' : 'OFF',
2519
+ };
2520
+ }
2521
+ }
2522
+ },
2523
+ },
2488
2524
  livolo_dimmer_state: {
2489
2525
  cluster: 'genPowerCfg',
2490
2526
  type: ['raw'],
@@ -2592,16 +2628,24 @@ const converters = {
2592
2628
  [124,210,21,216,128, 199,147,3,24,0,75,18,0, 19,7,0] after interview
2593
2629
  [122,209, 199,147,3,24,0,75,18,0, 7,1,6,1,0,11] off
2594
2630
  [122,209, 199,147,3,24,0,75,18,0, 7,1,6,1,1,11] on
2631
+
2595
2632
  new switch
2596
2633
  [124,210,21,216,128, 228,41,3,24,0,75,18,0, 19,1,0] after interview
2597
2634
  [122,209, 228,41,3,24,0,75,18,0, 7,1,0,1,0,11] off
2598
2635
  [122,209, 228,41,3,24,0,75,18,0, 7,1,0,1,1,11] on
2636
+
2599
2637
  old switch
2600
2638
  [124,210,21,216,128, 170, 10,2,24,0,75,18,0, 17,0,1] after interview
2601
2639
  [124,210,21,216,0, 18, 15,5,24,0,75,18,0, 34,0,0] left: 0, right: 0
2602
2640
  [124,210,21,216,0, 18, 15,5,24,0,75,18,0, 34,0,1] left: 1, right: 0
2603
2641
  [124,210,21,216,0, 18, 15,5,24,0,75,18,0, 34,0,2] left: 0, right: 1
2604
2642
  [124,210,21,216,0, 18, 15,5,24,0,75,18,0, 34,0,3] left: 1, right: 1
2643
+
2644
+ curtain switch
2645
+ [124,210,21,216,128, 110,74,116,33,0,75,18,0, 19,5,0] after interview
2646
+ [122,209, 110,74,116,33,0,75,18,0, 5,1,5,0,2,11] left: 0, right: 0 (off)
2647
+ [122,209, 110,74,116,33,0,75,18,0, 5,1,5,0,1,11] left: 1, right: 0 (left on)
2648
+ [122,209, 110,74,116,33,0,75,18,0, 5,1,5,0,0,11] left: 0, right: 1 (right on)
2605
2649
  */
2606
2650
  const malformedHeader = Buffer.from([0x7c, 0xd2, 0x15, 0xd8, 0x00]);
2607
2651
  const infoHeader = Buffer.from([0x7c, 0xd2, 0x15, 0xd8, 0x80]);
@@ -2630,6 +2674,12 @@ const converters = {
2630
2674
  meta.device.modelID = 'TI0001-switch-2gang';
2631
2675
  meta.device.save();
2632
2676
  }
2677
+ if (msg.data.includes(Buffer.from([19, 5, 0]), 13)) {
2678
+ if (meta.logger) meta.logger.debug('Detected Livolo Curtain Switch');
2679
+ // curtain switch, hack
2680
+ meta.device.modelID = 'TI0001-curtain-switch';
2681
+ meta.device.save();
2682
+ }
2633
2683
  if (msg.data.includes(Buffer.from([19, 20, 0]), 13)) {
2634
2684
  // new dimmer, hack
2635
2685
  meta.device.modelID = 'TI0001-dimmer';
@@ -3539,6 +3589,157 @@ const converters = {
3539
3589
  }
3540
3590
  },
3541
3591
  },
3592
+ tvtwo_thermostat: {
3593
+ cluster: 'manuSpecificTuya',
3594
+ type: ['commandGetData', 'commandSetDataResponse'],
3595
+ convert: (model, msg, publish, options, meta) => {
3596
+ const dp = msg.data.dp;
3597
+ const value = tuya.getDataValue(msg.data.datatype, msg.data.data);
3598
+ const presetLookup = {0: 'auto', 1: 'manual', 2: 'holiday'};
3599
+ switch (dp) {
3600
+ case tuya.dataPoints.tvMode:
3601
+ return {preset: presetLookup[value]};
3602
+ case tuya.dataPoints.tvChildLock:
3603
+ return {child_lock: value ? 'LOCK' : 'UNLOCK'};
3604
+ case tuya.dataPoints.tvHolidayTemp:
3605
+ return {holiday_temperature: (value / 10).toFixed(1)};
3606
+ case tuya.dataPoints.tvHeatingSetpoint:
3607
+ return {current_heating_setpoint: (value / 10).toFixed(1)};
3608
+ case tuya.dataPoints.tvFrostDetection:
3609
+ return {frost_protection: value ? 'ON' : 'OFF'};
3610
+ case tuya.dataPoints.tvWindowDetection:
3611
+ return {window_detection: value ? 'ON' : 'OFF'};
3612
+ case tuya.dataPoints.tvHeatingStop:
3613
+ return {heating_stop: value ? 'ON' : 'OFF'};
3614
+ case tuya.dataPoints.tvLocalTemp:
3615
+ return {local_temperature: (value / 10).toFixed(1)};
3616
+ case tuya.dataPoints.tvBattery:
3617
+ return {battery_low: value === 0 ? true : false};
3618
+ case tuya.dataPoints.tvTempCalibration:
3619
+ return {local_temperature_calibration: value > 55 ?
3620
+ ((value - 0x100000000)/10).toFixed(1): (value/ 10).toFixed(1)};
3621
+ case tuya.dataPoints.tvBoostTime:
3622
+ // Setting minimum 0 - maximum 465 seconds
3623
+ return {boost_timeset_countdown: value};
3624
+ case tuya.dataPoints.tvComfortTemp:
3625
+ return {comfort_temperature: (value / 10).toFixed(1)};
3626
+ case tuya.dataPoints.tvEcoTemp:
3627
+ return {eco_temperature: (value / 10).toFixed(1)};
3628
+ case tuya.dataPoints.tvOpenWindowTemp:
3629
+ return {open_window_temperature: (value / 10).toFixed(1)};
3630
+ case tuya.dataPoints.tvErrorStatus:
3631
+ return {fault_alarm: value};
3632
+ case tuya.dataPoints.tvHolidayMode:
3633
+ return {holiday_mode_date: value};
3634
+
3635
+ case tuya.dataPoints.tvBoostMode:
3636
+ // Online ?
3637
+ return {online: value ? 'ON' : 'OFF'};
3638
+ case tuya.dataPoints.tvWorkingDay:
3639
+ // tvWorkingDay: 31,
3640
+ return {working_day: value};
3641
+ case tuya.dataPoints.tvWeekSchedule:
3642
+ // tvWeekSchedule: 106, Week select 0 - 5 days, 1 - 6 days, 2 - 7 days
3643
+ return {week_schedule: value};
3644
+
3645
+ case tuya.dataPoints.tvMondaySchedule:
3646
+ return {schedule_monday:
3647
+ ' ' + value[0] / 6 + 'h:' + value[1] + 'm ' + value[2] / 10 + '°C' +
3648
+ ', ' + value[3] / 6 + 'h:' + value[4] + 'm ' + value[5] / 10 + '°C' +
3649
+ ', ' + value[6] / 6 + 'h:' + value[7] + 'm ' + value[8] / 10 + '°C' +
3650
+ ', ' + value[9] / 6 + 'h:' + value[10] + 'm ' + value[11] / 10 + '°C' +
3651
+ ', ' + value[12] / 6 + 'h:' + value[13] + 'm ' + value[14] / 10 + '°C ' +
3652
+ ', ' + value[15] / 6 + 'h:' + value[16] + 'm ' + value[17] / 10 + '°C ' +
3653
+ ', ' + value[18] / 6 + 'h:' + value[19] + 'm ' + value[20] / 10 + '°C ' +
3654
+ ', ' + value[21] / 6 + 'h:' + value[22] + 'm ' + value[23] / 10 + '°C ' +
3655
+ ', ' + value[24] / 6 + 'h:' + value[25] + 'm ' + value[26] / 10 + '°C ' +
3656
+ ', ' + value[27] / 6 + 'h:' + value[28] + 'm ' + value[29] / 10 + '°C ',
3657
+ };
3658
+ case tuya.dataPoints.tvTuesdaySchedule:
3659
+ return {schedule_tuesday:
3660
+ ' ' + value[0] / 6 + 'h:' + value[1] + 'm ' + value[2] / 10 + '°C' +
3661
+ ', ' + value[3] / 6 + 'h:' + value[4] + 'm ' + value[5] / 10 + '°C' +
3662
+ ', ' + value[6] / 6 + 'h:' + value[7] + 'm ' + value[8] / 10 + '°C' +
3663
+ ', ' + value[9] / 6 + 'h:' + value[10] + 'm ' + value[11] / 10 + '°C' +
3664
+ ', ' + value[12] / 6 + 'h:' + value[13] + 'm ' + value[14] / 10 + '°C ' +
3665
+ ', ' + value[15] / 6 + 'h:' + value[16] + 'm ' + value[17] / 10 + '°C ' +
3666
+ ', ' + value[18] / 6 + 'h:' + value[19] + 'm ' + value[20] / 10 + '°C ' +
3667
+ ', ' + value[21] / 6 + 'h:' + value[22] + 'm ' + value[23] / 10 + '°C ' +
3668
+ ', ' + value[24] / 6 + 'h:' + value[25] + 'm ' + value[26] / 10 + '°C ' +
3669
+ ', ' + value[27] / 6 + 'h:' + value[28] + 'm ' + value[29] / 10 + '°C ',
3670
+ };
3671
+ case tuya.dataPoints.tvWednesdaySchedule:
3672
+ return {schedule_wednesday:
3673
+ ' ' + value[0] / 6 + 'h:' + value[1] + 'm ' + value[2] / 10 + '°C' +
3674
+ ', ' + value[3] / 6 + 'h:' + value[4] + 'm ' + value[5] / 10 + '°C' +
3675
+ ', ' + value[6] / 6 + 'h:' + value[7] + 'm ' + value[8] / 10 + '°C' +
3676
+ ', ' + value[9] / 6 + 'h:' + value[10] + 'm ' + value[11] / 10 + '°C' +
3677
+ ', ' + value[12] / 6 + 'h:' + value[13] + 'm ' + value[14] / 10 + '°C ' +
3678
+ ', ' + value[15] / 6 + 'h:' + value[16] + 'm ' + value[17] / 10 + '°C ' +
3679
+ ', ' + value[18] / 6 + 'h:' + value[19] + 'm ' + value[20] / 10 + '°C ' +
3680
+ ', ' + value[21] / 6 + 'h:' + value[22] + 'm ' + value[23] / 10 + '°C ' +
3681
+ ', ' + value[24] / 6 + 'h:' + value[25] + 'm ' + value[26] / 10 + '°C ' +
3682
+ ', ' + value[27] / 6 + 'h:' + value[28] + 'm ' + value[29] / 10 + '°C ',
3683
+ };
3684
+ case tuya.dataPoints.tvThursdaySchedule:
3685
+ return {schedule_thursday:
3686
+ ' ' + value[0] / 6 + 'h:' + value[1] + 'm ' + value[2] / 10 + '°C' +
3687
+ ', ' + value[3] / 6 + 'h:' + value[4] + 'm ' + value[5] / 10 + '°C' +
3688
+ ', ' + value[6] / 6 + 'h:' + value[7] + 'm ' + value[8] / 10 + '°C' +
3689
+ ', ' + value[9] / 6 + 'h:' + value[10] + 'm ' + value[11] / 10 + '°C' +
3690
+ ', ' + value[12] / 6 + 'h:' + value[13] + 'm ' + value[14] / 10 + '°C ' +
3691
+ ', ' + value[15] / 6 + 'h:' + value[16] + 'm ' + value[17] / 10 + '°C ' +
3692
+ ', ' + value[18] / 6 + 'h:' + value[19] + 'm ' + value[20] / 10 + '°C ' +
3693
+ ', ' + value[21] / 6 + 'h:' + value[22] + 'm ' + value[23] / 10 + '°C ' +
3694
+ ', ' + value[24] / 6 + 'h:' + value[25] + 'm ' + value[26] / 10 + '°C ' +
3695
+ ', ' + value[27] / 6 + 'h:' + value[28] + 'm ' + value[29] / 10 + '°C ',
3696
+ };
3697
+ case tuya.dataPoints.tvFridaySchedule:
3698
+ return {schedule_friday:
3699
+ ' ' + value[0] / 6 + 'h:' + value[1] + 'm ' + value[2] / 10 + '°C' +
3700
+ ', ' + value[3] / 6 + 'h:' + value[4] + 'm ' + value[5] / 10 + '°C' +
3701
+ ', ' + value[6] / 6 + 'h:' + value[7] + 'm ' + value[8] / 10 + '°C' +
3702
+ ', ' + value[9] / 6 + 'h:' + value[10] + 'm ' + value[11] / 10 + '°C' +
3703
+ ', ' + value[12] / 6 + 'h:' + value[13] + 'm ' + value[14] / 10 + '°C ' +
3704
+ ', ' + value[15] / 6 + 'h:' + value[16] + 'm ' + value[17] / 10 + '°C ' +
3705
+ ', ' + value[18] / 6 + 'h:' + value[19] + 'm ' + value[20] / 10 + '°C ' +
3706
+ ', ' + value[21] / 6 + 'h:' + value[22] + 'm ' + value[23] / 10 + '°C ' +
3707
+ ', ' + value[24] / 6 + 'h:' + value[25] + 'm ' + value[26] / 10 + '°C ' +
3708
+ ', ' + value[27] / 6 + 'h:' + value[28] + 'm ' + value[29] / 10 + '°C ',
3709
+ };
3710
+ case tuya.dataPoints.tvSaturdaySchedule:
3711
+ return {schedule_saturday:
3712
+ ' ' + value[0] / 6 + 'h:' + value[1] + 'm ' + value[2] / 10 + '°C' +
3713
+ ', ' + value[3] / 6 + 'h:' + value[4] + 'm ' + value[5] / 10 + '°C' +
3714
+ ', ' + value[6] / 6 + 'h:' + value[7] + 'm ' + value[8] / 10 + '°C' +
3715
+ ', ' + value[9] / 6 + 'h:' + value[10] + 'm ' + value[11] / 10 + '°C' +
3716
+ ', ' + value[12] / 6 + 'h:' + value[13] + 'm ' + value[14] / 10 + '°C ' +
3717
+ ', ' + value[15] / 6 + 'h:' + value[16] + 'm ' + value[17] / 10 + '°C ' +
3718
+ ', ' + value[18] / 6 + 'h:' + value[19] + 'm ' + value[20] / 10 + '°C ' +
3719
+ ', ' + value[21] / 6 + 'h:' + value[22] + 'm ' + value[23] / 10 + '°C ' +
3720
+ ', ' + value[24] / 6 + 'h:' + value[25] + 'm ' + value[26] / 10 + '°C ' +
3721
+ ', ' + value[27] / 6 + 'h:' + value[28] + 'm ' + value[29] / 10 + '°C ',
3722
+ };
3723
+ case tuya.dataPoints.tvSundaySchedule:
3724
+ return {schedule_sunday:
3725
+ ' ' + value[0] / 6 + 'h:' + value[1] + 'm ' + value[2] / 10 + '°C' +
3726
+ ', ' + value[3] / 6 + 'h:' + value[4] + 'm ' + value[5] / 10 + '°C' +
3727
+ ', ' + value[6] / 6 + 'h:' + value[7] + 'm ' + value[8] / 10 + '°C' +
3728
+ ', ' + value[9] / 6 + 'h:' + value[10] + 'm ' + value[11] / 10 + '°C' +
3729
+ ', ' + value[12] / 6 + 'h:' + value[13] + 'm ' + value[14] / 10 + '°C ' +
3730
+ ', ' + value[15] / 6 + 'h:' + value[16] + 'm ' + value[17] / 10 + '°C ' +
3731
+ ', ' + value[18] / 6 + 'h:' + value[19] + 'm ' + value[20] / 10 + '°C ' +
3732
+ ', ' + value[21] / 6 + 'h:' + value[22] + 'm ' + value[23] / 10 + '°C ' +
3733
+ ', ' + value[24] / 6 + 'h:' + value[25] + 'm ' + value[26] / 10 + '°C ' +
3734
+ ', ' + value[27] / 6 + 'h:' + value[28] + 'm ' + value[29] / 10 + '°C ',
3735
+ };
3736
+
3737
+ default:
3738
+ meta.logger.warn(`zigbee-herdsman-converters:tvtwo_thermostat: NOT RECOGNIZED DP #${
3739
+ dp} with data ${JSON.stringify(msg.data)}`);
3740
+ }
3741
+ },
3742
+ },
3542
3743
  haozee_thermostat: {
3543
3744
  cluster: 'manuSpecificTuya',
3544
3745
  type: ['commandGetData', 'commandSetDataResponse'],
@@ -3554,7 +3755,7 @@ const converters = {
3554
3755
  case tuya.dataPoints.haozeeLocalTemp:
3555
3756
  return {local_temperature: (value / 10).toFixed(1)};
3556
3757
  case tuya.dataPoints.haozeeBoostHeatingCountdown:
3557
- // quick heating countdown
3758
+ // quick heating countdown - not supported by this device
3558
3759
  return {boost_heating_countdown: value};
3559
3760
  case tuya.dataPoints.haozeeWindowDetection:
3560
3761
  // window check
@@ -3631,11 +3832,11 @@ const converters = {
3631
3832
  // working status 0 - pause 1 -working
3632
3833
  return {'heating': value ? 'ON' : 'OFF'};
3633
3834
  case tuya.dataPoints.haozeeBoostHeating:
3634
- // rapid heating -> boolean
3635
- break;
3835
+ // rapid heating -> boolean - not supported by this device
3836
+ return {'boost_heating': value ? 'ON' : 'OFF'};
3636
3837
  case tuya.dataPoints.haozeeTempCalibration:
3637
3838
  // temperature calibration
3638
- break;
3839
+ return {'local_temperature_calibration': ( value/10 ).toFixed(1)};
3639
3840
  case tuya.dataPoints.haozeeValvePosition:
3640
3841
  // valve position
3641
3842
  return {'position': value};
@@ -3869,32 +4070,33 @@ const converters = {
3869
4070
  minutes: value[2],
3870
4071
  },
3871
4072
  };
3872
- case tuya.dataPoints.scheduleWorkday: // set schedule for workdays [6,0,20,8,0,15,11,30,15,12,30,15,17,30,20,22,0,15]
3873
- // 6:00 - 20*, 8:00 - 15*, 11:30 - 15*, 12:30 - 15*, 17:30 - 20*, 22:00 - 15*
3874
- // Top bits in hours have special meaning
3875
- // 8: ??
3876
- // 7: Current schedule indicator
3877
- return {workdays: [
3878
- {hour: value[0] & 0x3F, minute: value[1], temperature: value[2]},
3879
- {hour: value[3] & 0x3F, minute: value[4], temperature: value[5]},
3880
- {hour: value[6] & 0x3F, minute: value[7], temperature: value[8]},
3881
- {hour: value[9] & 0x3F, minute: value[10], temperature: value[11]},
3882
- {hour: value[12] & 0x3F, minute: value[13], temperature: value[14]},
3883
- {hour: value[15] & 0x3F, minute: value[16], temperature: value[17]},
3884
- ]};
3885
- case tuya.dataPoints.scheduleHoliday: // set schedule for holidays [6,0,20,8,0,15,11,30,15,12,30,15,17,30,20,22,0,15]
4073
+ case tuya.dataPoints.scheduleWorkday: // set schedule for workdays/holidays [6,0,20,8,0,15,11,30,15,12,30,15,17,30,20,22,0,15]
4074
+ case tuya.dataPoints.scheduleHoliday: {
3886
4075
  // 6:00 - 20*, 8:00 - 15*, 11:30 - 15*, 12:30 - 15*, 17:30 - 20*, 22:00 - 15*
3887
4076
  // Top bits in hours have special meaning
3888
- // 8: ??
3889
- // 7: Current schedule indicator
3890
- return {holidays: [
3891
- {hour: value[0] & 0x3F, minute: value[1], temperature: value[2]},
3892
- {hour: value[3] & 0x3F, minute: value[4], temperature: value[5]},
3893
- {hour: value[6] & 0x3F, minute: value[7], temperature: value[8]},
3894
- {hour: value[9] & 0x3F, minute: value[10], temperature: value[11]},
3895
- {hour: value[12] & 0x3F, minute: value[13], temperature: value[14]},
3896
- {hour: value[15] & 0x3F, minute: value[16], temperature: value[17]},
3897
- ]};
4077
+ // 6: Current schedule indicator
4078
+ const items = [];
4079
+ const programmingMode = [];
4080
+
4081
+ for (let i = 0; i < 6; i++) {
4082
+ const item = {hour: value[i*3] & 0x3F, minute: value[i*3+1], temperature: value[i*3+2]};
4083
+ if (value[i*3] & 0x40) {
4084
+ item['current'] = true;
4085
+ }
4086
+
4087
+ items[i] = item;
4088
+ programmingMode[i] =
4089
+ item['hour'].toString().padStart(2, '0') + ':' +
4090
+ item['minute'].toString().padStart(2, '0') + '/' +
4091
+ item['temperature'] + '°C';
4092
+ }
4093
+
4094
+ if (dp == tuya.dataPoints.scheduleWorkday) {
4095
+ return {workdays: items, workdays_schedule: programmingMode.join(' ')};
4096
+ } else {
4097
+ return {holidays: items, holidays_schedule: programmingMode.join(' ')};
4098
+ }
4099
+ }
3898
4100
  case tuya.dataPoints.childLock:
3899
4101
  return {child_lock: value ? 'LOCK' : 'UNLOCK'};
3900
4102
  case tuya.dataPoints.siterwellWindowDetection:
@@ -5810,6 +6012,11 @@ const converters = {
5810
6012
  };
5811
6013
 
5812
6014
  const data = utf8FromStr(msg['data']['16896']);
6015
+
6016
+ clearTimeout(globalStore.getValue(msg.endpoint, 'timer'));
6017
+ const timer = setTimeout(() => publish({action: 'lock', state: 'LOCK'}), 2 * 1000);
6018
+ globalStore.putValue(msg.endpoint, 'timer', timer);
6019
+
5813
6020
  return {
5814
6021
  action: 'unlock',
5815
6022
  action_user: data[3],
@@ -5818,6 +6025,77 @@ const converters = {
5818
6025
  };
5819
6026
  },
5820
6027
  },
6028
+ javis_microwave_sensor: {
6029
+ cluster: 'manuSpecificTuya',
6030
+ type: ['commandSetDataResponse', 'commandGetData'],
6031
+ convert: (model, msg, publish, options, meta) => {
6032
+ const dp = msg.data.dp;
6033
+ const value = tuya.getDataValue(msg.data.datatype, msg.data.data);
6034
+ const lookup = {
6035
+ 0: 'no_motion',
6036
+ 1: 'big_motion',
6037
+ 2: 'minor_motion',
6038
+ 3: 'breathing',
6039
+ 4: 'abnormal_state',
6040
+ 5: 'initializing',
6041
+ 6: 'initialization_completed',
6042
+ };
6043
+ switch (dp) {
6044
+ case 1:
6045
+ return {
6046
+ states: lookup[value],
6047
+ occupancy: (0 < value && value < 5) ? true: false,
6048
+ };
6049
+ case 2:
6050
+ return {
6051
+ sensitivity: value,
6052
+ };
6053
+ case 101:
6054
+ return {
6055
+ illuminance_lux: value,
6056
+ };
6057
+ case 102:
6058
+ if (meta.device.manufacturerName === '_TZE200_kagkgk0i') {
6059
+ return {
6060
+ illuminance_calibration: value,
6061
+ };
6062
+ } else {
6063
+ return {
6064
+ keep_time: value,
6065
+ };
6066
+ }
6067
+ case 103:
6068
+ return {
6069
+ led_enable: value == 1 ? true : false,
6070
+ };
6071
+ case 104:
6072
+ return {illuminance_lux: value};
6073
+ case 105:
6074
+ return {
6075
+ illuminance_calibration: value,
6076
+ };
6077
+ case 106:
6078
+ if (meta.device.manufacturerName === '_TZE200_kagkgk0i') {
6079
+ return {
6080
+ keep_time: value,
6081
+ };
6082
+ } else {
6083
+ break;
6084
+ }
6085
+ case 107:
6086
+ if (meta.device.manufacturerName === '_TZE200_kagkgk0i') {
6087
+ return {
6088
+ led_enable: value == 1 ? true : false,
6089
+ };
6090
+ } else {
6091
+ break;
6092
+ }
6093
+ default:
6094
+ meta.logger.warn(`zigbee-herdsman-converters:javis_microwave_sensor: NOT RECOGNIZED ` +
6095
+ `DP #${dp} with data ${JSON.stringify(msg.data)}`);
6096
+ }
6097
+ },
6098
+ },
5821
6099
  diyruz_freepad_config: {
5822
6100
  cluster: 'genOnOffSwitchCfg',
5823
6101
  type: ['readResponse'],
@@ -6047,12 +6325,34 @@ const converters = {
6047
6325
  DJT11LM_vibration: {
6048
6326
  cluster: 'closuresDoorLock',
6049
6327
  type: ['attributeReport', 'readResponse'],
6328
+ options: [exposes.options.vibration_timeout()],
6050
6329
  convert: (model, msg, publish, options, meta) => {
6051
6330
  const result = {};
6052
6331
 
6053
6332
  if (msg.data['85']) {
6054
6333
  const vibrationLookup = {1: 'vibration', 2: 'tilt', 3: 'drop'};
6055
6334
  result.action = vibrationLookup[msg.data['85']];
6335
+
6336
+ // Device only sends a message when vibration is detected.
6337
+ // Therefore we need to publish a no_vibration message on our own.
6338
+ if (result.action === 'vibration') {
6339
+ result.vibration = true;
6340
+
6341
+ const timeout = options && options.hasOwnProperty('vibration_timeout') ? options.vibration_timeout : 90;
6342
+
6343
+ // Stop any existing timer cause vibration detected
6344
+ clearTimeout(globalStore.getValue(msg.endpoint, 'vibration_timer', null));
6345
+ globalStore.putValue(msg.endpoint, 'vibration_timer', null);
6346
+
6347
+ // Set new timer to publish no_vibration message
6348
+ if (timeout !== 0) {
6349
+ const timer = setTimeout(() => {
6350
+ publish({vibration: false});
6351
+ }, timeout * 1000);
6352
+
6353
+ globalStore.putValue(msg.endpoint, 'vibration_timer', timer);
6354
+ }
6355
+ }
6056
6356
  }
6057
6357
 
6058
6358
  if (msg.data['1283']) {
@@ -15,6 +15,7 @@ const manufacturerOptions = {
15
15
  osram: {manufacturerCode: herdsman.Zcl.ManufacturerCode.OSRAM},
16
16
  eurotronic: {manufacturerCode: herdsman.Zcl.ManufacturerCode.JENNIC},
17
17
  danfoss: {manufacturerCode: herdsman.Zcl.ManufacturerCode.DANFOSS},
18
+ develco: {manufacturerCode: herdsman.Zcl.ManufacturerCode.DEVELCO},
18
19
  hue: {manufacturerCode: herdsman.Zcl.ManufacturerCode.PHILIPS},
19
20
  sinope: {manufacturerCode: herdsman.Zcl.ManufacturerCode.SINOPE_TECH},
20
21
  /*
@@ -2569,6 +2570,34 @@ const converters = {
2569
2570
  await entity.read('haDiagnostic', ['danfossMultimasterRole'], manufacturerOptions.danfoss);
2570
2571
  },
2571
2572
  },
2573
+ develco_pulse_configuration: {
2574
+ key: ['pulse_configuration'],
2575
+ convertSet: async (entity, key, value, meta) => {
2576
+ await entity.write('seMetering', {'develcoPulseConfiguration': value}, manufacturerOptions.develco);
2577
+ return {readAfterWriteTime: 200, state: {'pulse_configuration': value}};
2578
+ },
2579
+ convertGet: async (entity, key, meta) => {
2580
+ await entity.read('seMetering', ['develcoPulseConfiguration'], manufacturerOptions.develco);
2581
+ },
2582
+ },
2583
+ develco_interface_mode: {
2584
+ key: ['interface_mode'],
2585
+ convertSet: async (entity, key, value, meta) => {
2586
+ const payload = {'develcoInterfaceMode': utils.getKey(constants.develcoInterfaceMode, value, undefined, Number)};
2587
+ await entity.write('seMetering', payload, manufacturerOptions.develco);
2588
+ return {readAfterWriteTime: 200, state: {'interface_mode': value}};
2589
+ },
2590
+ convertGet: async (entity, key, meta) => {
2591
+ await entity.read('seMetering', ['develcoInterfaceMode'], manufacturerOptions.develco);
2592
+ },
2593
+ },
2594
+ develco_current_summation: {
2595
+ key: ['current_summation'],
2596
+ convertSet: async (entity, key, value, meta) => {
2597
+ await entity.write('seMetering', {'develcoCurrentSummation': value}, manufacturerOptions.develco);
2598
+ return {state: {'current_summation': value}};
2599
+ },
2600
+ },
2572
2601
  ZMCSW032D_cover_position: {
2573
2602
  key: ['position', 'tilt'],
2574
2603
  convertSet: async (entity, key, value, meta) => {
@@ -2735,10 +2764,79 @@ const converters = {
2735
2764
  await tuya.sendDataPointValue(entity, tuya.dataPoints.moesSminTempSet, temp);
2736
2765
  },
2737
2766
  },
2767
+ tvtwo_thermostat: {
2768
+ key: [
2769
+ 'window_detection', 'frost_protection', 'child_lock',
2770
+ 'current_heating_setpoint', 'local_temperature_calibration',
2771
+ 'holiday_temperature', 'comfort_temperature', 'eco_temperature',
2772
+ 'open_window_temperature', 'heating_stop', 'preset', 'boost_timeset_countdown',
2773
+ 'holiday_mode_date', 'working_day', 'week_schedule', 'week', 'online',
2774
+ ],
2775
+ convertSet: async (entity, key, value, meta) => {
2776
+ switch (key) {
2777
+ case 'preset': {
2778
+ const presetLookup = {'auto': 0, 'manual': 1, 'holiday': 3};
2779
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.tvMode, presetLookup[value]);
2780
+ return {state: {preset: value}};}
2781
+ case 'frost_protection':
2782
+ await tuya.sendDataPointBool(entity, tuya.dataPoints.tvFrostDetection, value === 'ON');
2783
+ break;
2784
+ case 'heating_stop':
2785
+ await tuya.sendDataPointBool(entity, tuya.dataPoints.tvHeatingStop, value === 'ON');
2786
+ break;
2787
+ case 'window_detection':
2788
+ await tuya.sendDataPointBool(entity, tuya.dataPoints.tvWindowDetection, value === 'ON');
2789
+ break;
2790
+ case 'child_lock':
2791
+ await tuya.sendDataPointBool(entity, tuya.dataPoints.tvChildLock, value === 'LOCK');
2792
+ break;
2793
+ case 'local_temperature_calibration':
2794
+ if (value > 0) value = value*10;
2795
+ if (value < 0) value = value*10 + 0x100000000;
2796
+ await tuya.sendDataPointValue(entity, tuya.dataPoints.tvTempCalibration, value);
2797
+ break;
2798
+ case 'current_heating_setpoint':
2799
+ await tuya.sendDataPointValue(entity, tuya.dataPoints.tvHeatingSetpoint, value * 10);
2800
+ break;
2801
+ case 'holiday_temperature':
2802
+ await tuya.sendDataPointValue(entity, tuya.dataPoints.tvHolidayTemp, value * 10);
2803
+ break;
2804
+ case 'comfort_temperature':
2805
+ await tuya.sendDataPointValue(entity, tuya.dataPoints.tvComfortTemp, value * 10);
2806
+ break;
2807
+ case 'eco_temperature':
2808
+ await tuya.sendDataPointValue(entity, tuya.dataPoints.tvEcoTemp, value * 10);
2809
+ break;
2810
+ case 'boost_timeset_countdown':
2811
+ // set min 0 - max 465 sec boost time
2812
+ await tuya.sendDataPointValue(entity, tuya.dataPoints.tvBoostTime, value);
2813
+ break;
2814
+ case 'open_window_temperature':
2815
+ await tuya.sendDataPointValue(entity, tuya.dataPoints.tvOpenWindowTemp, value * 10);
2816
+ break;
2817
+ case 'holiday_mode_date':
2818
+ await tuya.sendDataPointBitmap(entity, tuya.dataPoints.tvWorkingDayTimetvHolidayMode, value);
2819
+ break;
2820
+
2821
+ case 'online':
2822
+ // 115 ????? online
2823
+ await tuya.sendDataPointBool(entity, tuya.dataPoints.tvBoostMode, value === 'ON');
2824
+ break;
2825
+ case 'week': {
2826
+ const weekLookup = {'5+2': 0, '6+1': 1, '7': 2};
2827
+ const week = weekLookup[value];
2828
+ await tuya.sendDataPointEnum(entity, tuya.dataPoints.tvWorkingDay, week);
2829
+ return {state: {week: value}};}
2830
+
2831
+ default: // Unknown key
2832
+ meta.logger.warn(`toZigbee.tvtwo_thermostat: Unhandled key ${key}`);
2833
+ }
2834
+ },
2835
+ },
2738
2836
  haozee_thermostat_system_mode: {
2739
2837
  key: ['preset'],
2740
2838
  convertSet: async (entity, key, value, meta) => {
2741
- const lookup = {0: 'auto', 1: 'manual', 2: 'off', 3: 'on'};
2839
+ const lookup = {'auto': 0, 'manual': 1, 'off': 2, 'on': 3};
2742
2840
  await tuya.sendDataPointEnum(entity, tuya.dataPoints.haozeeSystemMode, lookup[value]);
2743
2841
  },
2744
2842
  },
@@ -2776,7 +2874,7 @@ const converters = {
2776
2874
  haozee_thermostat_temperature_calibration: {
2777
2875
  key: ['local_temperature_calibration'],
2778
2876
  convertSet: async (entity, key, value, meta) => {
2779
- let temp = Math.round(value * 1);
2877
+ let temp = Math.round(value * 10);
2780
2878
  if (temp < 0) {
2781
2879
  temp = 0xFFFFFFFF + temp + 1;
2782
2880
  }
@@ -4488,6 +4586,34 @@ const converters = {
4488
4586
  }
4489
4587
  },
4490
4588
  },
4589
+ tuya_thermostat_schedule_programming_mode: { // payload example "00:20/5°C 01:20/5°C 6:59/15°C 18:00/5°C 20:00/5°C 23:30/5°C"
4590
+ key: ['workdays_schedule', 'holidays_schedule'],
4591
+ convertSet: async (entity, key, value, meta) => {
4592
+ const dpId =
4593
+ (key === 'workdays_schedule') ?
4594
+ tuya.dataPoints.scheduleWorkday :
4595
+ tuya.dataPoints.scheduleHoliday;
4596
+ const payload = [];
4597
+ const items = value.split(' ');
4598
+
4599
+ for (let i = 0; i < 6; i++) {
4600
+ const hourTemperature = items[i].split('/');
4601
+ const hourMinute = hourTemperature[0].split(':', 2);
4602
+ const hour = parseInt(hourMinute[0]);
4603
+ const minute = parseInt(hourMinute[1]);
4604
+ const temperature = parseInt(hourTemperature[1]);
4605
+
4606
+ if (hour < 0 || hour >= 24 || minute < 0 || minute >= 60 || temperature < 5 || temperature >= 35) {
4607
+ throw new Error('Invalid hour, minute or temperature of:' + items[i]);
4608
+ }
4609
+
4610
+ payload[i*3] = hour;
4611
+ payload[i*3+1] = minute;
4612
+ payload[i*3+2] = temperature;
4613
+ }
4614
+ tuya.sendDataPointRaw(entity, dpId, payload);
4615
+ },
4616
+ },
4491
4617
  tuya_thermostat_week: {
4492
4618
  key: ['week'],
4493
4619
  convertSet: async (entity, key, value, meta) => {
@@ -6011,6 +6137,46 @@ const converters = {
6011
6137
  }
6012
6138
  },
6013
6139
  },
6140
+ javis_microwave_sensor: {
6141
+ key: [
6142
+ 'illuminance_calibration', 'led_enable',
6143
+ 'sensitivity', 'keep_time',
6144
+ ],
6145
+ convertSet: async (entity, key, value, meta) => {
6146
+ switch (key) {
6147
+ case 'illuminance_calibration':// (10--100) sensor illuminance sensitivity
6148
+ if (meta.device.manufacturerName === '_TZE200_kagkgk0i') {
6149
+ await tuya.sendDataPointRaw(entity, 102, [value]);
6150
+ break;
6151
+ } else {
6152
+ tuya.sendDataPointRaw(entity, 105, [value]);
6153
+ break;
6154
+ }
6155
+ case 'led_enable':// OK (value true/false or 1/0)
6156
+ if (meta.device.manufacturerName === '_TZE200_kagkgk0i') {
6157
+ await tuya.sendDataPointRaw(entity, 107, [value ? 1 : 0]);
6158
+ break;
6159
+ } else {
6160
+ await tuya.sendDataPointRaw(entity, 103, [value ? 1 : 0]);
6161
+ break;
6162
+ }
6163
+
6164
+ case 'sensitivity':// value: 25, 50, 75, 100
6165
+ await tuya.sendDataPointRaw(entity, 2, [value]);
6166
+ break;
6167
+ case 'keep_time': // value 0 --> 7 corresponding 5s, 30s, 1, 3, 5, 10, 20, 30 min
6168
+ if (meta.device.manufacturerName === '_TZE200_kagkgk0i') {
6169
+ await tuya.sendDataPointRaw(entity, 106, [value]);
6170
+ break;
6171
+ } else {
6172
+ await tuya.sendDataPointRaw(entity, 102, [value]);
6173
+ break;
6174
+ }
6175
+ default: // Unknown key
6176
+ throw new Error(`Unhandled key ${key}`);
6177
+ }
6178
+ },
6179
+ },
6014
6180
  moes_thermostat_tv: {
6015
6181
  key: [
6016
6182
  'system_mode', 'window_detection', 'frost_detection', 'child_lock',