Skip to content

Commit 0583f6c

Browse files
authored
Turn off schedule on system_mode change for _TZE200_c88teujp (#3867)
1 parent 947a8ae commit 0583f6c

File tree

2 files changed

+83
-24
lines changed

2 files changed

+83
-24
lines changed

tests/test_tuya_trv.py

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,24 @@
5050
)
5151

5252
TUYA_SYS_MODE_V01 = {
53-
Thermostat.SystemMode.Heat: b"\x01\x02\x00\x00\x02\x02\x04\x00\x01\x01",
54-
Thermostat.SystemMode.Off: b"\x01\x03\x00\x00\x03\x02\x04\x00\x01\x02",
53+
Thermostat.SystemMode.Heat: [b"\x01\x02\x00\x00\x02\x02\x04\x00\x01\x01"],
54+
Thermostat.SystemMode.Off: [b"\x01\x03\x00\x00\x03\x02\x04\x00\x01\x02"],
5555
}
5656

5757
TUYA_SYS_MODE_V02 = {
58-
Thermostat.SystemMode.Heat: b"\x01\x02\x00\x00\x02\x65\x01\x00\x01\x01",
59-
Thermostat.SystemMode.Off: b"\x01\x03\x00\x00\x03\x65\x01\x00\x01\x00",
58+
Thermostat.SystemMode.Heat: [
59+
b"\x01\x02\x00\x00\x02\x65\x01\x00\x01\x01",
60+
b"\x01\x03\x00\x00\x03\x6c\x01\x00\x01\x00",
61+
],
62+
Thermostat.SystemMode.Off: [
63+
b"\x01\x04\x00\x00\x04\x65\x01\x00\x01\x00",
64+
b"\x01\x05\x00\x00\x05\x6c\x01\x00\x01\x00",
65+
],
6066
}
6167

6268

6369
@pytest.mark.parametrize(
64-
"model, manuf, test_plan, set_pnt_msg, sys_mode_msg, ep_type",
70+
"model, manuf, test_plan, set_pnt_msg, sys_mode_msg, ep_type, set_schedule_off",
6571
(
6672
(
6773
"_TZE204_ogx8u5z6",
@@ -70,6 +76,7 @@
7076
TUYA_SP_V01,
7177
TUYA_SYS_MODE_V01,
7278
None, # test device has specific device type, real one has SMART_PLUG
79+
False,
7380
),
7481
(
7582
"_TZE200_3yp57tby",
@@ -78,6 +85,7 @@
7885
TUYA_SP_V02,
7986
TUYA_SYS_MODE_V02,
8087
zha.DeviceType.THERMOSTAT, # quirk replaces device type with THERMOSTAT
88+
True, # Enusure schedule is turned off
8189
),
8290
),
8391
)
@@ -89,6 +97,7 @@ async def test_handle_get_data(
8997
set_pnt_msg,
9098
sys_mode_msg,
9199
ep_type,
100+
set_schedule_off,
92101
):
93102
"""Test handle_get_data for multiple attributes."""
94103

@@ -152,41 +161,68 @@ async def async_success(*args, **kwargs):
152161
}
153162
)
154163
await wait_for_zigpy_tasks()
155-
m1.assert_called_with(
164+
165+
assert m1.call_args_list[0] == mock.call(
156166
cluster=0xEF00,
157167
sequence=2,
158-
data=sys_mode_msg[Thermostat.SystemMode.Heat],
168+
data=sys_mode_msg[Thermostat.SystemMode.Heat][0],
159169
command_id=0,
160170
timeout=5,
161171
expect_reply=False,
162172
use_ieee=False,
163173
ask_for_ack=None,
164174
priority=t.PacketPriority.NORMAL,
165175
)
176+
if set_schedule_off:
177+
# Ensure schedule_enable set to off
178+
assert m1.call_args_list[1] == mock.call(
179+
cluster=0xEF00,
180+
sequence=3,
181+
data=sys_mode_msg[Thermostat.SystemMode.Heat][1],
182+
command_id=0,
183+
timeout=5,
184+
expect_reply=False,
185+
use_ieee=False,
186+
ask_for_ack=None,
187+
priority=t.PacketPriority.NORMAL,
188+
)
189+
166190
assert status == [
167191
foundation.WriteAttributesStatusRecord(foundation.Status.SUCCESS)
168192
]
169193

170-
with mock.patch.object(
171-
ep.tuya_manufacturer.endpoint, "request", side_effect=async_success
172-
) as m1:
194+
m1.reset_mock()
195+
173196
(status,) = await ep.thermostat.write_attributes(
174197
{
175198
"system_mode": Thermostat.SystemMode.Off,
176199
}
177200
)
178201
await wait_for_zigpy_tasks()
179-
m1.assert_called_with(
202+
assert m1.call_args_list[0] == mock.call(
180203
cluster=0xEF00,
181-
sequence=3,
182-
data=sys_mode_msg[Thermostat.SystemMode.Off],
204+
sequence=2 + m1.call_count,
205+
data=sys_mode_msg[Thermostat.SystemMode.Off][0],
183206
command_id=0,
184207
timeout=5,
185208
expect_reply=False,
186209
use_ieee=False,
187210
ask_for_ack=None,
188211
priority=t.PacketPriority.NORMAL,
189212
)
213+
if set_schedule_off:
214+
# Ensure schedule_enable set to off
215+
assert m1.call_args_list[1] == mock.call(
216+
cluster=0xEF00,
217+
sequence=5,
218+
data=sys_mode_msg[Thermostat.SystemMode.Off][1],
219+
command_id=0,
220+
timeout=5,
221+
expect_reply=False,
222+
use_ieee=False,
223+
ask_for_ack=None,
224+
priority=t.PacketPriority.NORMAL,
225+
)
190226
assert status == [
191227
foundation.WriteAttributesStatusRecord(foundation.Status.SUCCESS)
192228
]

zhaquirks/tuya/tuya_trv.py

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Map from manufacturer to standard clusters for thermostatic valves."""
22

3+
from typing import Any
4+
35
from zigpy.profiles import zha
46
from zigpy.quirks.v2.homeassistant import PERCENTAGE, UnitOfTemperature
57
from zigpy.quirks.v2.homeassistant.binary_sensor import BinarySensorDeviceClass
@@ -79,6 +81,27 @@ def __init__(self, *args, **kwargs):
7981
)
8082

8183

84+
class TuyaThermostatV2NoSchedule(TuyaThermostatV2):
85+
"""Ensures schedule is disabled on system_mode change."""
86+
87+
async def write_attributes(
88+
self,
89+
attributes: dict[str | int, Any],
90+
manufacturer: int | None = None,
91+
**kwargs,
92+
) -> list:
93+
"""Catch attribute writes for system_mode and set schedule to off."""
94+
results = await super().write_attributes(attributes, manufacturer)
95+
if (
96+
Thermostat.AttributeDefs.system_mode.id in attributes
97+
or Thermostat.AttributeDefs.system_mode.name in attributes
98+
):
99+
tuya_cluster = self.endpoint.tuya_manufacturer
100+
await tuya_cluster.write_attributes({"schedule_enable": False})
101+
102+
return results
103+
104+
82105
(
83106
TuyaQuirkBuilder("_TYST11_KGbxAXL2", "GbxAXL2")
84107
.applies_to("_TYST11_c88teujp", "88teujp")
@@ -103,8 +126,8 @@ def __init__(self, *args, **kwargs):
103126
.replaces_endpoint(1, device_type=zha.DeviceType.THERMOSTAT)
104127
.tuya_dp(
105128
dp_id=3,
106-
ep_attribute=TuyaThermostatV2.ep_attribute,
107-
attribute_name=TuyaThermostatV2.AttributeDefs.running_state.name,
129+
ep_attribute=TuyaThermostatV2NoSchedule.ep_attribute,
130+
attribute_name=TuyaThermostatV2NoSchedule.AttributeDefs.running_state.name,
108131
converter=lambda x: RunningState.Heat_State_On if x else RunningState.Idle,
109132
)
110133
.tuya_switch(
@@ -121,8 +144,8 @@ def __init__(self, *args, **kwargs):
121144
)
122145
.tuya_number(
123146
dp_id=27,
124-
attribute_name=TuyaThermostatV2.AttributeDefs.local_temperature_calibration.name,
125-
type=t.int32s,
147+
attribute_name=TuyaThermostatV2NoSchedule.AttributeDefs.local_temperature_calibration.name,
148+
type=t.uint32_t,
126149
min_value=-6,
127150
max_value=6,
128151
unit=UnitOfTemperature.CELSIUS,
@@ -138,8 +161,8 @@ def __init__(self, *args, **kwargs):
138161
)
139162
.tuya_dp(
140163
dp_id=101,
141-
ep_attribute=TuyaThermostatV2.ep_attribute,
142-
attribute_name=TuyaThermostatV2.AttributeDefs.system_mode.name,
164+
ep_attribute=TuyaThermostatV2NoSchedule.ep_attribute,
165+
attribute_name=TuyaThermostatV2NoSchedule.AttributeDefs.system_mode.name,
143166
converter=lambda x: {
144167
True: Thermostat.SystemMode.Heat,
145168
False: Thermostat.SystemMode.Off,
@@ -151,18 +174,18 @@ def __init__(self, *args, **kwargs):
151174
)
152175
.tuya_dp(
153176
dp_id=102,
154-
ep_attribute=TuyaThermostatV2.ep_attribute,
155-
attribute_name=TuyaThermostatV2.AttributeDefs.local_temperature.name,
177+
ep_attribute=TuyaThermostatV2NoSchedule.ep_attribute,
178+
attribute_name=TuyaThermostatV2NoSchedule.AttributeDefs.local_temperature.name,
156179
converter=lambda x: x * 10,
157180
)
158181
.tuya_dp(
159182
dp_id=103,
160-
ep_attribute=TuyaThermostatV2.ep_attribute,
161-
attribute_name=TuyaThermostatV2.AttributeDefs.occupied_heating_setpoint.name,
183+
ep_attribute=TuyaThermostatV2NoSchedule.ep_attribute,
184+
attribute_name=TuyaThermostatV2NoSchedule.AttributeDefs.occupied_heating_setpoint.name,
162185
converter=lambda x: x * 10,
163186
dp_converter=lambda x: x // 10,
164187
)
165-
.adds(TuyaThermostatV2)
188+
.adds(TuyaThermostatV2NoSchedule)
166189
.tuya_sensor(
167190
dp_id=104,
168191
attribute_name="valve_position",

0 commit comments

Comments
 (0)