Home Assistant Unofficial Reference 2024.12.1
alarm_control_panel.py
Go to the documentation of this file.
1 """Support for Overkiz alarm control panel."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from typing import Any, cast
8 
9 from pyoverkiz.enums import OverkizCommand, OverkizCommandParam, OverkizState
10 from pyoverkiz.enums.ui import UIWidget
11 from pyoverkiz.types import StateType as OverkizStateType
12 
14  AlarmControlPanelEntity,
15  AlarmControlPanelEntityDescription,
16  AlarmControlPanelEntityFeature,
17  AlarmControlPanelState,
18 )
19 from homeassistant.config_entries import ConfigEntry
20 from homeassistant.const import Platform
21 from homeassistant.core import HomeAssistant
22 from homeassistant.helpers.entity import EntityDescription
23 from homeassistant.helpers.entity_platform import AddEntitiesCallback
24 
25 from . import HomeAssistantOverkizData
26 from .const import DOMAIN
27 from .coordinator import OverkizDataUpdateCoordinator
28 from .entity import OverkizDescriptiveEntity
29 
30 
31 @dataclass(frozen=True, kw_only=True)
33  """Class to describe an Overkiz alarm control panel."""
34 
35  supported_features: AlarmControlPanelEntityFeature
36  fn_state: Callable[[Callable[[str], OverkizStateType]], AlarmControlPanelState]
37 
38  alarm_disarm: str | None = None
39  alarm_disarm_args: OverkizStateType | list[OverkizStateType] = None
40  alarm_arm_home: str | None = None
41  alarm_arm_home_args: OverkizStateType | list[OverkizStateType] = None
42  alarm_arm_night: str | None = None
43  alarm_arm_night_args: OverkizStateType | list[OverkizStateType] = None
44  alarm_arm_away: str | None = None
45  alarm_arm_away_args: OverkizStateType | list[OverkizStateType] = None
46  alarm_trigger: str | None = None
47  alarm_trigger_args: OverkizStateType | list[OverkizStateType] = None
48 
49 
50 MAP_INTERNAL_STATUS_STATE: dict[str, AlarmControlPanelState] = {
51  OverkizCommandParam.OFF: AlarmControlPanelState.DISARMED,
52  OverkizCommandParam.ZONE_1: AlarmControlPanelState.ARMED_HOME,
53  OverkizCommandParam.ZONE_2: AlarmControlPanelState.ARMED_NIGHT,
54  OverkizCommandParam.TOTAL: AlarmControlPanelState.ARMED_AWAY,
55 }
56 
57 
59  select_state: Callable[[str], OverkizStateType],
60 ) -> AlarmControlPanelState:
61  """Return the state of the device."""
62  if (
63  cast(str, select_state(OverkizState.INTERNAL_INTRUSION_DETECTED))
64  == OverkizCommandParam.DETECTED
65  ):
66  return AlarmControlPanelState.TRIGGERED
67 
68  if cast(str, select_state(OverkizState.INTERNAL_CURRENT_ALARM_MODE)) != cast(
69  str, select_state(OverkizState.INTERNAL_TARGET_ALARM_MODE)
70  ):
71  return AlarmControlPanelState.PENDING
72 
73  return MAP_INTERNAL_STATUS_STATE[
74  cast(str, select_state(OverkizState.INTERNAL_TARGET_ALARM_MODE))
75  ]
76 
77 
78 MAP_CORE_ACTIVE_ZONES: dict[str, AlarmControlPanelState] = {
79  OverkizCommandParam.A: AlarmControlPanelState.ARMED_HOME,
80  f"{OverkizCommandParam.A},{OverkizCommandParam.B}": AlarmControlPanelState.ARMED_NIGHT,
81  f"{OverkizCommandParam.A},{OverkizCommandParam.B},{OverkizCommandParam.C}": AlarmControlPanelState.ARMED_AWAY,
82 }
83 
84 
86  select_state: Callable[[str], OverkizStateType],
87 ) -> AlarmControlPanelState:
88  """Return the state of the device."""
89  if state := cast(str, select_state(OverkizState.CORE_ACTIVE_ZONES)):
90  # The Stateful Alarm Controller has 3 zones with the following options:
91  # (A, B, C, A,B, B,C, A,C, A,B,C). Since it is not possible to map this to AlarmControlPanel entity,
92  # only the most important zones are mapped, other zones can only be disarmed.
93  if state in MAP_CORE_ACTIVE_ZONES:
94  return MAP_CORE_ACTIVE_ZONES[state]
95 
96  return AlarmControlPanelState.ARMED_CUSTOM_BYPASS
97 
98  return AlarmControlPanelState.DISARMED
99 
100 
101 MAP_MYFOX_STATUS_STATE: dict[str, AlarmControlPanelState] = {
102  OverkizCommandParam.ARMED: AlarmControlPanelState.ARMED_AWAY,
103  OverkizCommandParam.DISARMED: AlarmControlPanelState.DISARMED,
104  OverkizCommandParam.PARTIAL: AlarmControlPanelState.ARMED_NIGHT,
105 }
106 
107 
109  select_state: Callable[[str], OverkizStateType],
110 ) -> AlarmControlPanelState:
111  """Return the state of the device."""
112  if (
113  cast(str, select_state(OverkizState.CORE_INTRUSION))
114  == OverkizCommandParam.DETECTED
115  ):
116  return AlarmControlPanelState.TRIGGERED
117 
118  return MAP_MYFOX_STATUS_STATE[
119  cast(str, select_state(OverkizState.MYFOX_ALARM_STATUS))
120  ]
121 
122 
123 MAP_ARM_TYPE: dict[str, AlarmControlPanelState] = {
124  OverkizCommandParam.DISARMED: AlarmControlPanelState.DISARMED,
125  OverkizCommandParam.ARMED_DAY: AlarmControlPanelState.ARMED_HOME,
126  OverkizCommandParam.ARMED_NIGHT: AlarmControlPanelState.ARMED_NIGHT,
127  OverkizCommandParam.ARMED: AlarmControlPanelState.ARMED_AWAY,
128 }
129 
130 
132  select_state: Callable[[str], OverkizStateType],
133 ) -> AlarmControlPanelState:
134  """Return the state of the device."""
135  return MAP_ARM_TYPE[
136  cast(str, select_state(OverkizState.VERISURE_ALARM_PANEL_MAIN_ARM_TYPE))
137  ]
138 
139 
140 ALARM_DESCRIPTIONS: list[OverkizAlarmDescription] = [
141  # TSKAlarmController
142  # Disabled by default since all Overkiz hubs have this
143  # virtual device, but only a few users actually use this.
145  key=UIWidget.TSKALARM_CONTROLLER,
146  entity_registry_enabled_default=False,
147  supported_features=(
148  AlarmControlPanelEntityFeature.ARM_AWAY
149  | AlarmControlPanelEntityFeature.ARM_HOME
150  | AlarmControlPanelEntityFeature.ARM_NIGHT
151  | AlarmControlPanelEntityFeature.TRIGGER
152  ),
153  fn_state=_state_tsk_alarm_controller,
154  alarm_disarm=OverkizCommand.ALARM_OFF,
155  alarm_arm_home=OverkizCommand.SET_TARGET_ALARM_MODE,
156  alarm_arm_home_args=OverkizCommandParam.PARTIAL_1,
157  alarm_arm_night=OverkizCommand.SET_TARGET_ALARM_MODE,
158  alarm_arm_night_args=OverkizCommandParam.PARTIAL_2,
159  alarm_arm_away=OverkizCommand.SET_TARGET_ALARM_MODE,
160  alarm_arm_away_args=OverkizCommandParam.TOTAL,
161  alarm_trigger=OverkizCommand.ALARM_ON,
162  ),
163  # StatefulAlarmController
165  key=UIWidget.STATEFUL_ALARM_CONTROLLER,
166  supported_features=(
167  AlarmControlPanelEntityFeature.ARM_AWAY
168  | AlarmControlPanelEntityFeature.ARM_HOME
169  | AlarmControlPanelEntityFeature.ARM_NIGHT
170  ),
171  fn_state=_state_stateful_alarm_controller,
172  alarm_disarm=OverkizCommand.ALARM_OFF,
173  alarm_arm_home=OverkizCommand.ALARM_ZONE_ON,
174  alarm_arm_home_args=OverkizCommandParam.A,
175  alarm_arm_night=OverkizCommand.ALARM_ZONE_ON,
176  alarm_arm_night_args=f"{OverkizCommandParam.A}, {OverkizCommandParam.B}",
177  alarm_arm_away=OverkizCommand.ALARM_ZONE_ON,
178  alarm_arm_away_args=(
179  f"{OverkizCommandParam.A},{OverkizCommandParam.B},{OverkizCommandParam.C}"
180  ),
181  ),
182  # MyFoxAlarmController
184  key=UIWidget.MY_FOX_ALARM_CONTROLLER,
185  supported_features=AlarmControlPanelEntityFeature.ARM_AWAY
186  | AlarmControlPanelEntityFeature.ARM_NIGHT,
187  fn_state=_state_myfox_alarm_controller,
188  alarm_disarm=OverkizCommand.DISARM,
189  alarm_arm_night=OverkizCommand.PARTIAL,
190  alarm_arm_away=OverkizCommand.ARM,
191  ),
192  # AlarmPanelController
194  key=UIWidget.ALARM_PANEL_CONTROLLER,
195  supported_features=(
196  AlarmControlPanelEntityFeature.ARM_AWAY
197  | AlarmControlPanelEntityFeature.ARM_HOME
198  | AlarmControlPanelEntityFeature.ARM_NIGHT
199  ),
200  fn_state=_state_alarm_panel_controller,
201  alarm_disarm=OverkizCommand.DISARM,
202  alarm_arm_home=OverkizCommand.ARM_PARTIAL_DAY,
203  alarm_arm_night=OverkizCommand.ARM_PARTIAL_NIGHT,
204  alarm_arm_away=OverkizCommand.ARM,
205  ),
206 ]
207 
208 SUPPORTED_DEVICES = {description.key: description for description in ALARM_DESCRIPTIONS}
209 
210 
212  hass: HomeAssistant,
213  entry: ConfigEntry,
214  async_add_entities: AddEntitiesCallback,
215 ) -> None:
216  """Set up the Overkiz alarm control panel from a config entry."""
217  data: HomeAssistantOverkizData = hass.data[DOMAIN][entry.entry_id]
218 
221  device.device_url,
222  data.coordinator,
223  description,
224  )
225  for device in data.platforms[Platform.ALARM_CONTROL_PANEL]
226  if (
227  description := SUPPORTED_DEVICES.get(device.widget)
228  or SUPPORTED_DEVICES.get(device.ui_class)
229  )
230  )
231 
232 
234  """Representation of an Overkiz Alarm Control Panel."""
235 
236  entity_description: OverkizAlarmDescription
237  _attr_code_arm_required = False
238 
239  def __init__(
240  self,
241  device_url: str,
242  coordinator: OverkizDataUpdateCoordinator,
243  description: EntityDescription,
244  ) -> None:
245  """Initialize the device."""
246  super().__init__(device_url, coordinator, description)
247 
248  self._attr_supported_features_attr_supported_features = self.entity_descriptionentity_description.supported_features
249 
250  @property
251  def alarm_state(self) -> AlarmControlPanelState:
252  """Return the state of the device."""
253  return self.entity_descriptionentity_description.fn_state(self.executorexecutor.select_state)
254 
255  async def async_alarm_disarm(self, code: str | None = None) -> None:
256  """Send disarm command."""
257  assert self.entity_descriptionentity_description.alarm_disarm
258  await self.async_execute_commandasync_execute_command(
259  self.entity_descriptionentity_description.alarm_disarm,
260  self.entity_descriptionentity_description.alarm_disarm_args,
261  )
262 
263  async def async_alarm_arm_home(self, code: str | None = None) -> None:
264  """Send arm home command."""
265  assert self.entity_descriptionentity_description.alarm_arm_home
266  await self.async_execute_commandasync_execute_command(
267  self.entity_descriptionentity_description.alarm_arm_home,
268  self.entity_descriptionentity_description.alarm_arm_home_args,
269  )
270 
271  async def async_alarm_arm_night(self, code: str | None = None) -> None:
272  """Send arm night command."""
273  assert self.entity_descriptionentity_description.alarm_arm_night
274  await self.async_execute_commandasync_execute_command(
275  self.entity_descriptionentity_description.alarm_arm_night,
276  self.entity_descriptionentity_description.alarm_arm_night_args,
277  )
278 
279  async def async_alarm_arm_away(self, code: str | None = None) -> None:
280  """Send arm away command."""
281  assert self.entity_descriptionentity_description.alarm_arm_away
282  await self.async_execute_commandasync_execute_command(
283  self.entity_descriptionentity_description.alarm_arm_away,
284  self.entity_descriptionentity_description.alarm_arm_away_args,
285  )
286 
287  async def async_alarm_trigger(self, code: str | None = None) -> None:
288  """Send alarm trigger command."""
289  assert self.entity_descriptionentity_description.alarm_trigger
290  await self.async_execute_commandasync_execute_command(
291  self.entity_descriptionentity_description.alarm_trigger,
292  self.entity_descriptionentity_description.alarm_trigger_args,
293  )
294 
295  async def async_execute_command(self, command_name: str, args: Any) -> None:
296  """Execute device command in async context."""
297  if args:
298  await self.executorexecutor.async_execute_command(command_name, args)
299  else:
300  await self.executorexecutor.async_execute_command(command_name)
None __init__(self, str device_url, OverkizDataUpdateCoordinator coordinator, EntityDescription description)
AlarmControlPanelState _state_myfox_alarm_controller(Callable[[str], OverkizStateType] select_state)
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
AlarmControlPanelState _state_tsk_alarm_controller(Callable[[str], OverkizStateType] select_state)
AlarmControlPanelState _state_alarm_panel_controller(Callable[[str], OverkizStateType] select_state)
AlarmControlPanelState _state_stateful_alarm_controller(Callable[[str], OverkizStateType] select_state)