Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
336 changes: 69 additions & 267 deletions src/devices/tuya.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20829,296 +20829,98 @@ export const definitions: DefinitionWithExtend[] = [
],
},
},

{
fingerprint: tuya.fingerprint("TS0601", ["_TZE284_4cqhd2ha", "_TZE200_8ply8mjj"]),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a lot of unexpected changes here, e.g. with this PR _TZE200_8ply8mjj becomes unsupported, could you clean it up?

model: "TZE284_4cqhd2ha",
vendor: "Girier",
description: "Vibration sensor",
extend: [tuya.modernExtend.tuyaBase({dp: true})],
whiteLabel: [tuya.whitelabel("Conecto", "COZIGVS", "Vibration sensor", ["_TZE200_8ply8mjj"])],
fingerprint: tuya.fingerprint("TS0601", ["_TZE284_ty5neqqo"]),
model: "TRV60_thermostat",
vendor: "AVATTO",
description: "Screen thermostatic radiator valve",
extend: [tuya.modernExtend.tuyaBase({dp: true, forceTimeUpdates: true})],
exposes: [
e.vibration(),
e.enum("mode", ea.STATE_SET, ["auto", "manual"]).withDescription("Mode"),
e.enum("work_state", ea.STATE_SET, ["opened", "closed"]).withDescription("Work state"),
e.child_lock(),
e.battery(),
e.window_detection(),
e
.numeric("sensitivity", ea.STATE)
.withValueMin(0)
.withValueMax(2)
.climate()
.withSetpoint("current_heating_setpoint", 5, 35, 0.5, ea.STATE_SET)
.withLocalTemperature(ea.STATE)
.withLocalTemperatureCalibration(-3, 3, 1, ea.STATE_SET),
e.numeric("fault", ea.STATE).withDescription("Raw fault code"),
e
.binary("frost_protection", ea.STATE_SET, "ON", "OFF")
.withDescription(
"Sensitivity of the sensor (single press the button when muted to switch between" +
" low (one beep), medium (two beeps) and max (three beeps))",
"When the room temperature is lower than 5 °C, the valve opens; when the temperature rises to 8 °C, the valve closes.",
),
e.text("buzzer_mute", ea.STATE).withDescription("ON when buzzer is muted (double press the button on device to toggle)"),
],
meta: {
tuyaDatapoints: [
[1, "vibration", tuya.valueConverter.trueFalse1],
[101, "sensitivity", tuya.valueConverter.raw],
[103, "buzzer_mute", tuya.valueConverter.onOff],
],
},
},
{
fingerprint: tuya.fingerprint("TS0601", ["_TZE200_fodv6bkr"]),
model: "RM28-LE",
vendor: "Ronco",
description: "Zigbee roller shade motor",
extend: [tuya.modernExtend.tuyaBase({dp: true})],
options: [exposes.options.invert_cover()],
exposes: [
e.battery(),
e.cover_position().setAccess("position", ea.STATE_SET),
e.enum("reverse_direction", ea.STATE_SET, ["forward", "back"]).withDescription("Reverse the motor direction"),
e.text("work_state", ea.STATE),
e.enum("click_control", ea.STATE_SET, ["up", "down"]).withDescription("Single motor steps"),
e.enum("border", ea.STATE_SET, ["up", "down", "up_delete", "down_delete", "remove_top_bottom"]),
e.binary("motor_fault", ea.STATE, true, false),
],
meta: {
// All datapoints go in here
tuyaDatapoints: [
[
1,
"state",
tuya.valueConverterBasic.lookup({
OPEN: tuya.enum(0),
STOP: tuya.enum(1),
CLOSE: tuya.enum(2),
}),
],
[2, "position", tuya.valueConverter.coverPosition],
[3, "position", tuya.valueConverter.coverPosition],
[5, "reverse_direction", tuya.valueConverterBasic.lookup({forward: tuya.enum(0), back: tuya.enum(1)})],
[
7,
"work_state",
tuya.valueConverterBasic.lookup((options) =>
options.invert_cover ? {opening: tuya.enum(0), closing: tuya.enum(1)} : {opening: tuya.enum(1), closing: tuya.enum(0)},
),
],
[12, "motor_fault", tuya.valueConverter.trueFalse1],
[13, "battery", tuya.valueConverter.raw],
[
16,
"border",
tuya.valueConverterBasic.lookup({
up: tuya.enum(0),
down: tuya.enum(1),
up_delete: tuya.enum(2),
down_delete: tuya.enum(3),
remove_top_bottom: tuya.enum(4),
}),
],
[20, "click_control", tuya.valueConverterBasic.lookup({up: tuya.enum(0), down: tuya.enum(1)})],
],
},
},
{
fingerprint: tuya.fingerprint("TS0601", ["_TZE284_iwyqtclw"]),
model: "M9Pro",
vendor: "Tuya",
description: "Smart 4 gang switch, curtain, smart light or scene. 1x thermosat control",
exposes: [
...[1, 2, 3, 4].map((i) => e.switch().withEndpoint(`l${i}`).setAccess("state", ea.STATE_SET)),
...[1, 2, 3].map((i) =>
e.enum("mode", ea.STATE_SET, ["switch", "scene", "smart_light", "curtain"]).withEndpoint(`l${i}`).withDescription(`Switch ${i} mode`),
),
e.binary("scale_protection", ea.STATE_SET, "ON", "OFF"),
e.numeric("valve_volume", ea.STATE).withDescription("The current percentage of valve flow rate."),
e
.enum("mode", ea.STATE_SET, ["switch", "scene", "smart_light", "curtain", "thermostat"])
.withEndpoint("l4")
.withDescription("Switch 4 mode"),
...[1, 2, 3, 4].map((i) =>
e.text("name", ea.STATE_SET).withEndpoint(`l${i}`).withDescription(`Name for Switch ${i} (max 8 chars displayed)`),
),
...[1, 2, 3, 4].map((i) =>
e.text("scene_name", ea.STATE_SET).withEndpoint(`l${i}`).withDescription(`Scene name for switch ${i} (max 8 chars displayed)`),
),
...[1, 2, 3, 4].map((i) =>
e.text("dimmer_name", ea.STATE_SET).withEndpoint(`l${i}`).withDescription(`Smart Light name for switch ${i} (max 8 chars displayed)`),
),
...[1, 2, 3, 4].map((i) =>
exposes.binary("dimmer_switch", ea.STATE_SET, "ON", "OFF").withEndpoint(`l${i}`).withDescription(`Smart Light - toggle switch ${i}`),
),
...[1, 2, 3, 4].map((i) =>
e.text("curtain_name", ea.STATE_SET).withEndpoint(`l${i}`).withDescription(`Curtain name for switch ${i} (max 8 chars displayed)`),
),
...[1, 2, 3, 4].map((i) =>
exposes.binary("curtain_switch", ea.STATE_SET, "ON", "OFF").withEndpoint(`l${i}`).withDescription(`Curtain - toggle switch ${i}`),
),
e.power_on_behavior(["off", "on", "previous"]).withAccess(ea.STATE_SET).withDescription("Whole panel override.").withCategory("config"),
...[1, 2, 3, 4].map((i) => e.power_on_behavior(["off", "on", "previous"]).withAccess(ea.STATE_SET).withEndpoint(`l${i}`)),
exposes.binary("show_weather", ea.STATE_SET, "ON", "OFF").withDescription("Show time and weather (on) or just switch names (off)"),
exposes.binary("backlight", ea.STATE_SET, "ON", "OFF").withDescription("Button LED backlights"),
e.enum("show_screen", ea.STATE_SET, ["motion", "on_press", "on"]).withDescription("Screen display mode"),
// Thermostat - Switch 4 only
exposes
.binary("thermostat", ea.STATE_SET, "ON", "OFF")
.withDescription("Thermostat - toggle switch"),
e.text("thermostat_name", ea.STATE_SET).withDescription("Name for Thermostat (max 8 chars displayed)"),

.numeric("humidity", ea.STATE)
.withDescription("The percentage of humidity collected after adding an external temperature and humidity sensor."),
e
.enum("scene_switch", ea.STATE_SET, [
"switch_1",
"switch_2",
"switch_3",
"switch_4",
"switch_5",
"switch_6",
"switch_7",
"switch_8",
"switch_9",
])
.withDescription("Scene Switch"),
e.action(["scene_0", "scene_1", "scene_2", "scene_3", "scene_4", "scene_5", "scene_6", "scene_7", "scene_8"]),
// Set weather
// Temperature will accept decimal values e.g. 0.1 but display will round off to nearest whole number.
// Best to handle rounding in HA before passing to z2m.
e
.numeric("temperature_1", ea.STATE_SET)
.withValueMin(-65)
.withValueMax(99)
.withDescription("Temperature")
.withValueStep(0.1),
// If you need other values to match your weather provider, map them with a template in HA or
// add a z2m 'External Extension' to override tuya.M8ProTuyaWeatherCondition.
.binary("out_door_sensor1", ea.STATE, "ON", "OFF")
.withDescription("The on-off status of the door magnet after adding the first external door magnet sensor."),
e
.binary("out_door_sensor2", ea.STATE, "ON", "OFF")
.withDescription("The on-off status of the door magnet after adding the second external door magnet sensor."),
e
.binary("out_door_sensor3", ea.STATE, "ON", "OFF")
.withDescription("The on-off status of the door magnet after adding the third external door magnet sensor."),
e
.enum("condition_1", ea.STATE_SET, Object.keys(tuya.M8ProTuyaWeatherCondition))
.withDescription("Weather condition"),
.numeric("out_temperature", ea.STATE)
.withDescription("The percentage of temperature collected after adding an external temperature and humidity sensor."),
e.enum("screen_orientation", ea.STATE_SET, ["normal", "inverted"]).withDescription("Screen orientation"),
],
meta: {
multiEndpoint: true,
disableDefaultResponse: true,
tuyaDatapoints: [
[1, "action", tuya.valueConverter.static("scene_1")],
[2, "action", tuya.valueConverter.static("scene_2")],
[3, "action", tuya.valueConverter.static("scene_3")],
[4, "action", tuya.valueConverter.static("scene_4")],
[17, "action", tuya.valueConverter.static("scene_0")],
[
18,
"mode_l1",
tuya.valueConverterBasic.lookup({
switch: tuya.enum(0),
scene: tuya.enum(1),
smart_light: tuya.enum(2),
curtain: tuya.enum(3),
}),
],
[
19,
"mode_l2",
tuya.valueConverterBasic.lookup({
switch: tuya.enum(0),
scene: tuya.enum(1),
smart_light: tuya.enum(2),
curtain: tuya.enum(3),
}),
],
[
20,
"mode_l3",
tuya.valueConverterBasic.lookup({
switch: tuya.enum(0),
scene: tuya.enum(1),
smart_light: tuya.enum(2),
curtain: tuya.enum(3),
}),
],
[
21,
"mode_l4",
2,
"mode",
tuya.valueConverterBasic.lookup({
switch: tuya.enum(0),
scene: tuya.enum(1),
smart_light: tuya.enum(2),
curtain: tuya.enum(3),
thermostat: tuya.enum(4),
auto: tuya.enum(0),
manual: tuya.enum(1),
}),
],
[24, "state_l1", tuya.valueConverter.onOff],
[25, "state_l2", tuya.valueConverter.onOff],
[26, "state_l3", tuya.valueConverter.onOff],
[27, "state_l4", tuya.valueConverter.onOff],
[36, "show_weather", tuya.valueConverter.onOff],
// Screen display modes.
// From Tuya Dev Platform: enum "range": ["relay", "pos", "none"]. These names don't match what's happening on the device
[
37,
"show_screen",
3,
"work_state",
tuya.valueConverterBasic.lookup({
motion: tuya.enum(0), // Screen on/off with motion detection.
on_press: tuya.enum(1), // Disable motion detection. Only show screen on physical button press.
on: tuya.enum(2), // Screen stays on.
opened: tuya.enum(0),
closed: tuya.enum(1),
}),
],

[38, "power_on_behavior", tuya.valueConverter.powerOnBehaviorEnum], // whole panel
[39, "power_on_behavior_l1", tuya.valueConverter.powerOnBehaviorEnum],
[40, "power_on_behavior_l2", tuya.valueConverter.powerOnBehaviorEnum],
[41, "power_on_behavior_l3", tuya.valueConverter.powerOnBehaviorEnum],
[42, "power_on_behavior_l4", tuya.valueConverter.powerOnBehaviorEnum],
[101, "backlight", tuya.valueConverter.onOff], // Buttton backlights
[
102,
"scene_switch",
[5, "local_temperature", tuya.valueConverter.divideBy10],
[6, "battery", tuya.valueConverter.raw],
[7, "child_lock", tuya.valueConverter.lockUnlock],
[14, "window_detection", tuya.valueConverter.onOff],
[28, "schedule_monday", tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(1)],
[29, "schedule_tuesday", tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(2)],
[30, "schedule_wednesday", tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(3)],
[31, "schedule_thursday", tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(4)],
[32, "schedule_friday", tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(5)],
[33, "schedule_saturday", tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(6)],
[34, "schedule_sunday", tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(7)],
[35, "fault", tuya.valueConverter.errorOrBatteryLow],
[36, "frost_protection", tuya.valueConverter.onOff],
[36, "scale_protection", tuya.valueConverter.onOff],
[47, "local_temperature_calibration", tuya.valueConverter.localTempCalibration1],
[101, "valve_volume", tuya.valueConverter.raw],
[102, "humidity", tuya.valueConverter.raw],
[103, "out_door_sensor1", tuya.valueConverter.onOff],
[106, "out_door_sensor2", tuya.valueConverter.onOff],
[107, "out_door_sensor3", tuya.valueConverter.onOff],
[109, "out_temperature", tuya.valueConverter.divideBy10],
[
117,
"screen_orientation",
tuya.valueConverterBasic.lookup({
switch_1: tuya.enum(0),
switch_2: tuya.enum(1),
switch_3: tuya.enum(2),
switch_4: tuya.enum(3),
switch_5: tuya.enum(4),
switch_6: tuya.enum(5),
switch_7: tuya.enum(6),
switch_8: tuya.enum(7),
switch_9: tuya.enum(8),
normal: tuya.enum(0),
inverted: tuya.enum(1),
}),
],

// The M9 Pro only displays first 8 chars of names, but state will hold the whole string.
// I prefer that the state always equals what is displayed, so trim name to 8 chars.
// Switch names
[103, "name_l1", convLocal.nameTrunc],
[104, "name_l2", convLocal.nameTrunc],
[105, "name_l3", convLocal.nameTrunc],
[106, "name_l4", convLocal.nameTrunc],

// Scene names
[107, "scene_name_l1", convLocal.nameTrunc],
[108, "scene_name_l2", convLocal.nameTrunc],
[109, "scene_name_l3", convLocal.nameTrunc],
[110, "scene_name_l4", convLocal.nameTrunc],

// Dimmer names
[111, "dimmer_name_l1", convLocal.nameTrunc],
[112, "dimmer_name_l2", convLocal.nameTrunc],
[113, "dimmer_name_l3", convLocal.nameTrunc],
[114, "dimmer_name_l4", convLocal.nameTrunc],

// Curtain names
[115, "curtain_name_l1", convLocal.nameTrunc],
[116, "curtain_name_l2", convLocal.nameTrunc],
[117, "curtain_name_l3", convLocal.nameTrunc],
[118, "curtain_name_l4", convLocal.nameTrunc],

[119, "curtain_switch_l1", tuya.valueConverter.onOff],
[120, "curtain_switch_l2", tuya.valueConverter.onOff],
[121, "curtain_switch_l3", tuya.valueConverter.onOff],
[122, "curtain_switch_l4", tuya.valueConverter.onOff],
[123, "dimmer_switch_l1", tuya.valueConverter.onOff],
[124, "dimmer_switch_l2", tuya.valueConverter.onOff],
[125, "dimmer_switch_l3", tuya.valueConverter.onOff],
[126, "dimmer_switch_l4", tuya.valueConverter.onOff],
[127, "thermostat", tuya.valueConverter.onOff],
[128, "thermostat_name", convLocal.nameTrunc],
[123, "current_heating_setpoint", tuya.valueConverter.divideBy10],
],
},

extend: [
// NOTE: M9 Pro only updates the weather condition and temperature on power cycle and once per hour.
tuya.modernExtend.tuyaWeatherForecast({includeCurrentWeather: true, numberOfForecastDays: 3, correctForNegativeValues: false}),
tuya.modernExtend.tuyaBase({
dp: true,
timeStart: "1970", // needed else date/time doesn't sync with z2m > 2.6.2
}),
m.deviceEndpoints({endpoints: {l1: 1, l2: 1, l3: 1, l4: 1}}),
],
},
];