2121from .const import TUYA_DISCOVERY_NEW , DeviceCategory , DPCode
2222from .entity import TuyaEntity
2323from .models import DPCodeEnumWrapper , DPCodeRawWrapper
24+ from .type_information import EnumTypeInformation
2425
2526ALARM : 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