Home Assistant Unofficial Reference 2024.12.1
alarm_control_panel.py
Go to the documentation of this file.
1 """Support for NX584 alarm control panels."""
2 
3 from __future__ import annotations
4 
5 from datetime import timedelta
6 import logging
7 
8 from nx584 import client
9 import requests
10 import voluptuous as vol
11 
13  PLATFORM_SCHEMA as ALARM_CONTROL_PANEL_PLATFORM_SCHEMA,
14  AlarmControlPanelEntity,
15  AlarmControlPanelEntityFeature,
16  AlarmControlPanelState,
17  CodeFormat,
18 )
19 from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT
20 from homeassistant.core import HomeAssistant
21 from homeassistant.exceptions import PlatformNotReady
22 from homeassistant.helpers import config_validation as cv, entity_platform
23 from homeassistant.helpers.entity_platform import AddEntitiesCallback
24 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
25 
26 _LOGGER = logging.getLogger(__name__)
27 
28 SCAN_INTERVAL = timedelta(seconds=10)
29 
30 DEFAULT_HOST = "localhost"
31 DEFAULT_NAME = "NX584"
32 DEFAULT_PORT = 5007
33 SERVICE_BYPASS_ZONE = "bypass_zone"
34 SERVICE_UNBYPASS_ZONE = "unbypass_zone"
35 ATTR_ZONE = "zone"
36 
37 PLATFORM_SCHEMA = ALARM_CONTROL_PANEL_PLATFORM_SCHEMA.extend(
38  {
39  vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
40  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
41  vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
42  }
43 )
44 
45 
47  hass: HomeAssistant,
48  config: ConfigType,
49  async_add_entities: AddEntitiesCallback,
50  discovery_info: DiscoveryInfoType | None = None,
51 ) -> None:
52  """Set up the NX584 platform."""
53  name: str = config[CONF_NAME]
54  host: str = config[CONF_HOST]
55  port: int = config[CONF_PORT]
56 
57  url = f"http://{host}:{port}"
58 
59  try:
60  alarm_client = client.Client(url)
61  await hass.async_add_executor_job(alarm_client.list_zones)
62  except requests.exceptions.ConnectionError as ex:
63  _LOGGER.error(
64  "Unable to connect to %(host)s: %(reason)s",
65  {"host": url, "reason": ex},
66  )
67  raise PlatformNotReady from ex
68 
69  entity = NX584Alarm(name, alarm_client, url)
70  async_add_entities([entity])
71 
72  platform = entity_platform.async_get_current_platform()
73 
74  platform.async_register_entity_service(
75  SERVICE_BYPASS_ZONE,
76  {vol.Required(ATTR_ZONE): cv.positive_int},
77  "alarm_bypass",
78  )
79 
80  platform.async_register_entity_service(
81  SERVICE_UNBYPASS_ZONE,
82  {vol.Required(ATTR_ZONE): cv.positive_int},
83  "alarm_unbypass",
84  )
85 
86 
88  """Representation of a NX584-based alarm panel."""
89 
90  _attr_code_format = CodeFormat.NUMBER
91  _attr_supported_features = (
92  AlarmControlPanelEntityFeature.ARM_HOME
93  | AlarmControlPanelEntityFeature.ARM_AWAY
94  )
95  _attr_code_arm_required = False
96 
97  def __init__(self, name: str, alarm_client: client.Client, url: str) -> None:
98  """Init the nx584 alarm panel."""
99  self._attr_name_attr_name = name
100  self._alarm_alarm = alarm_client
101  self._url_url = url
102 
103  def update(self) -> None:
104  """Process new events from panel."""
105  try:
106  part = self._alarm_alarm.list_partitions()[0]
107  zones = self._alarm_alarm.list_zones()
108  except requests.exceptions.ConnectionError as ex:
109  _LOGGER.error(
110  "Unable to connect to %(host)s: %(reason)s",
111  {"host": self._url_url, "reason": ex},
112  )
113  self._attr_alarm_state_attr_alarm_state = None
114  zones = []
115  except IndexError:
116  _LOGGER.error("NX584 reports no partitions")
117  self._attr_alarm_state_attr_alarm_state = None
118  zones = []
119 
120  bypassed = False
121  for zone in zones:
122  if zone["bypassed"]:
123  _LOGGER.debug(
124  "Zone %(zone)s is bypassed, assuming HOME",
125  {"zone": zone["number"]},
126  )
127  bypassed = True
128  break
129 
130  if not part["armed"]:
131  self._attr_alarm_state_attr_alarm_state = AlarmControlPanelState.DISARMED
132  elif bypassed:
133  self._attr_alarm_state_attr_alarm_state = AlarmControlPanelState.ARMED_HOME
134  else:
135  self._attr_alarm_state_attr_alarm_state = AlarmControlPanelState.ARMED_AWAY
136 
137  for flag in part["condition_flags"]:
138  if flag == "Siren on":
139  self._attr_alarm_state_attr_alarm_state = AlarmControlPanelState.TRIGGERED
140 
141  def alarm_disarm(self, code: str | None = None) -> None:
142  """Send disarm command."""
143  self._alarm_alarm.disarm(code)
144 
145  def alarm_arm_home(self, code: str | None = None) -> None:
146  """Send arm home command."""
147  self._alarm_alarm.arm("stay")
148 
149  def alarm_arm_away(self, code: str | None = None) -> None:
150  """Send arm away command."""
151  self._alarm_alarm.arm("exit")
152 
153  def alarm_bypass(self, zone: int) -> None:
154  """Send bypass command."""
155  self._alarm_alarm.set_bypass(zone, True)
156 
157  def alarm_unbypass(self, zone: int) -> None:
158  """Send bypass command."""
159  self._alarm_alarm.set_bypass(zone, False)
None __init__(self, str name, client.Client alarm_client, str url)
None async_setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback async_add_entities, DiscoveryInfoType|None discovery_info=None)