Skip to content

Commit 6152e0f

Browse files
authored
Split action and state wrapper in Tuya alarm control panel (home-assistant#158532)
1 parent f1a8974 commit 6152e0f

File tree

1 file changed

+38
-23
lines changed

1 file changed

+38
-23
lines changed

homeassistant/components/tuya/alarm_control_panel.py

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode
2222
from .entity import TuyaEntity
2323
from .models import DPCodeEnumWrapper, DPCodeRawWrapper
24+
from .type_information import EnumTypeInformation
2425

2526
ALARM: dict[DeviceCategory, tuple[AlarmControlPanelEntityDescription, ...]] = {
2627
DeviceCategory.MAL: (
@@ -48,21 +49,14 @@ def read_device_status(self, device: CustomerDevice) -> str | None:
4849
return status.decode("utf-16be")
4950

5051

51-
class _AlarmModeWrapper(DPCodeEnumWrapper):
52-
"""Wrapper for the alarm mode of a device.
52+
class _AlarmStateWrapper(DPCodeEnumWrapper):
53+
"""Wrapper for the alarm state of a device.
5354
5455
Handles alarm mode enum values and determines the alarm state,
5556
including logic for detecting when the alarm is triggered and
5657
distinguishing triggered state from battery warnings.
5758
"""
5859

59-
_ACTION_MAPPINGS = {
60-
# Home Assistant action => Tuya device mode
61-
"arm_home": "home",
62-
"arm_away": "arm",
63-
"disarm": "disarmed",
64-
"trigger": "sos",
65-
}
6660
_STATE_MAPPINGS = {
6761
# Tuya device mode => Home Assistant panel state
6862
"disarmed": AlarmControlPanelState.DISARMED,
@@ -71,7 +65,9 @@ class _AlarmModeWrapper(DPCodeEnumWrapper):
7165
"sos": AlarmControlPanelState.TRIGGERED,
7266
}
7367

74-
def read_panel_state(self, device: CustomerDevice) -> AlarmControlPanelState | None:
68+
def read_device_status(
69+
self, device: CustomerDevice
70+
) -> AlarmControlPanelState | None:
7571
"""Read the device status."""
7672
# When the alarm is triggered, only its 'state' is changing. From 'normal' to 'alarm'.
7773
# The 'mode' doesn't change, and stays as 'arm' or 'home'.
@@ -84,10 +80,22 @@ def read_panel_state(self, device: CustomerDevice) -> AlarmControlPanelState | N
8480
):
8581
return AlarmControlPanelState.TRIGGERED
8682

87-
if (status := self.read_device_status(device)) is None:
83+
if (status := super().read_device_status(device)) is None:
8884
return None
8985
return self._STATE_MAPPINGS.get(status)
9086

87+
88+
class _AlarmActionWrapper(DPCodeEnumWrapper):
89+
"""Wrapper for setting the alarm mode of a device."""
90+
91+
_ACTION_MAPPINGS = {
92+
# Home Assistant action => Tuya device mode
93+
"arm_home": "home",
94+
"arm_away": "arm",
95+
"disarm": "disarmed",
96+
"trigger": "sos",
97+
}
98+
9199
def supports_action(self, action: str) -> bool:
92100
"""Return if action is supported."""
93101
return (
@@ -123,14 +131,19 @@ def async_discover_device(device_ids: list[str]) -> None:
123131
device,
124132
manager,
125133
description,
126-
mode_wrapper=mode_wrapper,
134+
action_wrapper=_AlarmActionWrapper(
135+
master_mode.dpcode, master_mode
136+
),
127137
changed_by_wrapper=_AlarmChangedByWrapper.find_dpcode(
128138
device, DPCode.ALARM_MSG
129139
),
140+
state_wrapper=_AlarmStateWrapper(
141+
master_mode.dpcode, master_mode
142+
),
130143
)
131144
for description in descriptions
132145
if (
133-
mode_wrapper := _AlarmModeWrapper.find_dpcode(
146+
master_mode := EnumTypeInformation.find_dpcode(
134147
device, DPCode.MASTER_MODE, prefer_function=True
135148
)
136149
)
@@ -156,28 +169,30 @@ def __init__(
156169
device_manager: Manager,
157170
description: AlarmControlPanelEntityDescription,
158171
*,
159-
mode_wrapper: _AlarmModeWrapper,
172+
action_wrapper: _AlarmActionWrapper,
160173
changed_by_wrapper: _AlarmChangedByWrapper | None,
174+
state_wrapper: _AlarmStateWrapper,
161175
) -> None:
162176
"""Init Tuya Alarm."""
163177
super().__init__(device, device_manager)
164178
self.entity_description = description
165179
self._attr_unique_id = f"{super().unique_id}{description.key}"
166-
self._mode_wrapper = mode_wrapper
180+
self._action_wrapper = action_wrapper
167181
self._changed_by_wrapper = changed_by_wrapper
182+
self._state_wrapper = state_wrapper
168183

169184
# Determine supported modes
170-
if mode_wrapper.supports_action("arm_home"):
185+
if action_wrapper.supports_action("arm_home"):
171186
self._attr_supported_features |= AlarmControlPanelEntityFeature.ARM_HOME
172-
if mode_wrapper.supports_action("arm_away"):
187+
if action_wrapper.supports_action("arm_away"):
173188
self._attr_supported_features |= AlarmControlPanelEntityFeature.ARM_AWAY
174-
if mode_wrapper.supports_action("trigger"):
189+
if action_wrapper.supports_action("trigger"):
175190
self._attr_supported_features |= AlarmControlPanelEntityFeature.TRIGGER
176191

177192
@property
178193
def alarm_state(self) -> AlarmControlPanelState | None:
179194
"""Return the state of the device."""
180-
return self._mode_wrapper.read_panel_state(self.device)
195+
return self._read_wrapper(self._state_wrapper)
181196

182197
@property
183198
def changed_by(self) -> str | None:
@@ -186,16 +201,16 @@ def changed_by(self) -> str | None:
186201

187202
async def async_alarm_disarm(self, code: str | None = None) -> None:
188203
"""Send Disarm command."""
189-
await self._async_send_wrapper_updates(self._mode_wrapper, "disarm")
204+
await self._async_send_wrapper_updates(self._action_wrapper, "disarm")
190205

191206
async def async_alarm_arm_home(self, code: str | None = None) -> None:
192207
"""Send Home command."""
193-
await self._async_send_wrapper_updates(self._mode_wrapper, "arm_home")
208+
await self._async_send_wrapper_updates(self._action_wrapper, "arm_home")
194209

195210
async def async_alarm_arm_away(self, code: str | None = None) -> None:
196211
"""Send Arm command."""
197-
await self._async_send_wrapper_updates(self._mode_wrapper, "arm_away")
212+
await self._async_send_wrapper_updates(self._action_wrapper, "arm_away")
198213

199214
async def async_alarm_trigger(self, code: str | None = None) -> None:
200215
"""Send SOS command."""
201-
await self._async_send_wrapper_updates(self._mode_wrapper, "trigger")
216+
await self._async_send_wrapper_updates(self._action_wrapper, "trigger")

0 commit comments

Comments
 (0)