Skip to content

Commit 00aad65

Browse files
authored
Add Tuya Namron thermostat _TZE204_p3lqqy2r (#3625)
1 parent 207e19e commit 00aad65

File tree

2 files changed

+292
-0
lines changed

2 files changed

+292
-0
lines changed

tests/test_tuya_thermostat.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"""Tests for Tuya Thermostat."""
2+
3+
import pytest
4+
from zigpy.zcl import foundation
5+
from zigpy.zcl.clusters.hvac import Thermostat
6+
7+
from tests.common import ClusterListener
8+
import zhaquirks
9+
from zhaquirks.tuya.mcu import TuyaMCUCluster
10+
11+
zhaquirks.setup()
12+
13+
14+
@pytest.mark.parametrize(
15+
"msg,attr,value",
16+
[
17+
(
18+
b"\t\x13\x02\x00\x06\x01\x01\x00\x01\x01",
19+
Thermostat.AttributeDefs.system_mode,
20+
Thermostat.SystemMode.Heat,
21+
), # Set to heat, dp 1
22+
(
23+
b"\t\x16\x02\x00\t\x18\x02\x00\x04\x00\x00\x00\x18",
24+
Thermostat.AttributeDefs.local_temperature,
25+
2400,
26+
), # Current temp 24, dp 24
27+
(
28+
b"\t\x15\x02\x00\x08\x10\x02\x00\x04\x00\x00\x00\x19",
29+
Thermostat.AttributeDefs.occupied_heating_setpoint,
30+
2500,
31+
), # Setpoint to 25, dp 16
32+
(
33+
b"\t\x17\x02\x00\n\x1c\x02\x00\x04\x00\x00\x00\x00",
34+
Thermostat.AttributeDefs.local_temperature_calibration,
35+
0,
36+
), # Local calibration to 0, dp 28
37+
(
38+
b"\t\x1c\x02\x00\x0fh\x01\x00\x01\x01",
39+
Thermostat.AttributeDefs.running_state,
40+
Thermostat.RunningState.Heat_State_On,
41+
), # Running state, dp 104
42+
(
43+
b"\t\x1d\x02\x00\x10k\x02\x00\x04\x00\x00\x00\x1b",
44+
Thermostat.AttributeDefs.max_heat_setpoint_limit,
45+
2700,
46+
), # Max heat set point, dp 107
47+
],
48+
)
49+
async def test_handle_get_data(zigpy_device_from_v2_quirk, msg, attr, value):
50+
"""Test handle_get_data for multiple attributes."""
51+
52+
quirked = zigpy_device_from_v2_quirk("_TZE204_p3lqqy2r", "TS0601")
53+
ep = quirked.endpoints[1]
54+
55+
assert ep.tuya_manufacturer is not None
56+
assert isinstance(ep.tuya_manufacturer, TuyaMCUCluster)
57+
58+
assert ep.thermostat is not None
59+
assert isinstance(ep.thermostat, Thermostat)
60+
61+
thermostat_listener = ClusterListener(ep.thermostat)
62+
63+
hdr, data = ep.tuya_manufacturer.deserialize(msg)
64+
status = ep.tuya_manufacturer.handle_get_data(data.data)
65+
assert status == foundation.Status.SUCCESS
66+
67+
assert len(thermostat_listener.attribute_updates) == 1
68+
assert thermostat_listener.attribute_updates[0][0] == attr.id
69+
assert thermostat_listener.attribute_updates[0][1] == value
70+
71+
assert ep.thermostat.get(attr.id) == value
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
"""Tuya TS0601 Thermostat."""
2+
3+
from zigpy.quirks.v2.homeassistant import (
4+
UnitOfElectricCurrent,
5+
UnitOfElectricPotential,
6+
UnitOfEnergy,
7+
UnitOfPower,
8+
UnitOfTemperature,
9+
)
10+
from zigpy.quirks.v2.homeassistant.sensor import SensorDeviceClass, SensorStateClass
11+
from zigpy.types import t
12+
from zigpy.zcl import foundation
13+
from zigpy.zcl.clusters.hvac import Thermostat
14+
15+
from zhaquirks.tuya.builder import TuyaQuirkBuilder
16+
from zhaquirks.tuya.mcu import TuyaAttributesCluster
17+
18+
19+
class RegulatorPeriod(t.enum8):
20+
"""Tuya Regulator Period enum."""
21+
22+
FifteenMin = 0x00
23+
ThirtyMin = 0x01
24+
FortyFiveMin = 0x02
25+
SixtyMin = 0x03
26+
NinetyMin = 0x04
27+
28+
29+
class ThermostatMode(t.enum8):
30+
"""Tuya Thermostat mode."""
31+
32+
Regulator = 0x00
33+
Thermostat = 0x01
34+
35+
36+
class PresetMode(t.enum8):
37+
"""Tuya PresetMode enum."""
38+
39+
Manual = 0x00
40+
Home = 0x01
41+
Away = 0x02
42+
43+
44+
class SensorMode(t.enum8):
45+
"""Tuya SensorMode enum."""
46+
47+
Air = 0x00
48+
Floor = 0x01
49+
Both = 0x02
50+
51+
52+
class TuyaThermostat(Thermostat, TuyaAttributesCluster):
53+
"""Tuya local thermostat cluster."""
54+
55+
manufacturer_id_override: t.uint16_t = foundation.ZCLHeader.NO_MANUFACTURER_ID
56+
57+
_CONSTANT_ATTRIBUTES = {
58+
Thermostat.AttributeDefs.ctrl_sequence_of_oper.id: Thermostat.ControlSequenceOfOperation.Heating_Only
59+
}
60+
61+
def __init__(self, *args, **kwargs):
62+
"""Init a TuyaThermostat cluster."""
63+
super().__init__(*args, **kwargs)
64+
self.add_unsupported_attribute(
65+
Thermostat.AttributeDefs.setpoint_change_source.id
66+
)
67+
self.add_unsupported_attribute(
68+
Thermostat.AttributeDefs.setpoint_change_source_timestamp.id
69+
)
70+
self.add_unsupported_attribute(Thermostat.AttributeDefs.pi_heating_demand.id)
71+
72+
73+
(
74+
TuyaQuirkBuilder("_TZE204_p3lqqy2r", "TS0601")
75+
.tuya_dp(
76+
dp_id=1,
77+
ep_attribute=TuyaThermostat.ep_attribute,
78+
attribute_name=TuyaThermostat.AttributeDefs.system_mode.name,
79+
converter=lambda x: 0x00 if not x else 0x04,
80+
dp_converter=lambda x: x != 0x00,
81+
)
82+
.tuya_enum(
83+
dp_id=2,
84+
attribute_name="preset_mode",
85+
enum_class=PresetMode,
86+
translation_key="preset_mode",
87+
fallback_name="Preset mode",
88+
)
89+
.tuya_dp(
90+
dp_id=16,
91+
ep_attribute=TuyaThermostat.ep_attribute,
92+
attribute_name=TuyaThermostat.AttributeDefs.occupied_heating_setpoint.name,
93+
converter=lambda x: x * 100,
94+
dp_converter=lambda x: x // 100,
95+
)
96+
.tuya_dp(
97+
dp_id=24,
98+
ep_attribute=TuyaThermostat.ep_attribute,
99+
attribute_name=TuyaThermostat.AttributeDefs.local_temperature.name,
100+
converter=lambda x: x * 100,
101+
)
102+
.tuya_dp(
103+
dp_id=28,
104+
ep_attribute=TuyaThermostat.ep_attribute,
105+
attribute_name=Thermostat.AttributeDefs.local_temperature_calibration.name,
106+
converter=lambda x: x * 100,
107+
dp_converter=lambda x: x // 100,
108+
)
109+
.tuya_switch(
110+
dp_id=30,
111+
attribute_name="child_lock",
112+
translation_key="child_lock",
113+
fallback_name="Child lock",
114+
)
115+
.tuya_sensor(
116+
dp_id=101,
117+
attribute_name="local_temperature_floor",
118+
type=t.int16s,
119+
device_class=SensorDeviceClass.TEMPERATURE,
120+
state_class=SensorStateClass.MEASUREMENT,
121+
unit=UnitOfTemperature.CELSIUS,
122+
translation_key="local_temperature_floor",
123+
fallback_name="Floor temperature",
124+
)
125+
.tuya_enum(
126+
dp_id=102,
127+
attribute_name="temperature_sensor_select",
128+
enum_class=SensorMode,
129+
translation_key="sensor_mode",
130+
fallback_name="Sensor mode",
131+
)
132+
.tuya_dp(
133+
dp_id=104,
134+
ep_attribute=TuyaThermostat.ep_attribute,
135+
attribute_name=TuyaThermostat.AttributeDefs.running_state.name,
136+
converter=lambda x: 0x00 if not x else 0x01,
137+
)
138+
.tuya_binary_sensor(
139+
dp_id=106,
140+
attribute_name="window_detection",
141+
translation_key="window_detection",
142+
fallback_name="Open window detection",
143+
)
144+
.tuya_dp(
145+
dp_id=107,
146+
ep_attribute=TuyaThermostat.ep_attribute,
147+
attribute_name=TuyaThermostat.AttributeDefs.max_heat_setpoint_limit.name,
148+
converter=lambda x: x * 100,
149+
dp_converter=lambda x: x // 100,
150+
)
151+
.tuya_enum(
152+
dp_id=108,
153+
attribute_name="thermostat_mode",
154+
enum_class=ThermostatMode,
155+
translation_key="thermostat_mode",
156+
fallback_name="Mode",
157+
)
158+
.tuya_enum(
159+
dp_id=109,
160+
attribute_name="regulator_period",
161+
enum_class=RegulatorPeriod,
162+
translation_key="regulator_period",
163+
fallback_name="Regulator period",
164+
)
165+
.tuya_number(
166+
dp_id=110,
167+
attribute_name="regulator_set_point",
168+
type=t.uint16_t,
169+
unit=UnitOfTemperature.CELSIUS,
170+
min_value=0,
171+
max_value=100,
172+
step=1,
173+
translation_key="regulator_set_point",
174+
fallback_name="Regulator set point",
175+
)
176+
.adds(TuyaThermostat)
177+
.tuya_sensor(
178+
dp_id=120,
179+
attribute_name="current",
180+
type=t.int16s,
181+
divisor=10,
182+
device_class=SensorDeviceClass.CURRENT,
183+
state_class=SensorStateClass.MEASUREMENT,
184+
unit=UnitOfElectricCurrent.AMPERE,
185+
translation_key="current",
186+
fallback_name="Current",
187+
)
188+
.tuya_sensor(
189+
dp_id=121,
190+
attribute_name="voltage",
191+
type=t.int16s,
192+
device_class=SensorDeviceClass.VOLTAGE,
193+
state_class=SensorStateClass.MEASUREMENT,
194+
unit=UnitOfElectricPotential.VOLT,
195+
translation_key="voltage",
196+
fallback_name="Voltage",
197+
)
198+
.tuya_sensor(
199+
dp_id=122,
200+
attribute_name="power",
201+
type=t.int16s,
202+
device_class=SensorDeviceClass.POWER,
203+
state_class=SensorStateClass.MEASUREMENT,
204+
unit=UnitOfPower.WATT,
205+
translation_key="power",
206+
fallback_name="Power",
207+
)
208+
.tuya_sensor(
209+
dp_id=123,
210+
attribute_name="energy",
211+
type=t.int16s,
212+
divisor=100,
213+
device_class=SensorDeviceClass.ENERGY,
214+
state_class=SensorStateClass.TOTAL,
215+
unit=UnitOfEnergy.KILO_WATT_HOUR,
216+
translation_key="energy",
217+
fallback_name="Energy",
218+
)
219+
.skip_configuration()
220+
.add_to_registry()
221+
)

0 commit comments

Comments
 (0)