Followup PR for SIA integration (#51108)
* Updates based on Martin's review * fix strings and cleaned up constants
This commit is contained in:
parent
f0952d3ee8
commit
cede36d91c
9 changed files with 114 additions and 131 deletions
|
@ -2,18 +2,14 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Callable
|
from typing import Any
|
||||||
|
|
||||||
from pysiaalarm import SIAEvent
|
from pysiaalarm import SIAEvent
|
||||||
|
|
||||||
from homeassistant.components.alarm_control_panel import (
|
from homeassistant.components.alarm_control_panel import AlarmControlPanelEntity
|
||||||
ENTITY_ID_FORMAT as ALARM_ENTITY_ID_FORMAT,
|
|
||||||
AlarmControlPanelEntity,
|
|
||||||
)
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_PORT,
|
CONF_PORT,
|
||||||
CONF_ZONE,
|
|
||||||
STATE_ALARM_ARMED_AWAY,
|
STATE_ALARM_ARMED_AWAY,
|
||||||
STATE_ALARM_ARMED_CUSTOM_BYPASS,
|
STATE_ALARM_ARMED_CUSTOM_BYPASS,
|
||||||
STATE_ALARM_ARMED_NIGHT,
|
STATE_ALARM_ARMED_NIGHT,
|
||||||
|
@ -21,8 +17,10 @@ from homeassistant.const import (
|
||||||
STATE_ALARM_TRIGGERED,
|
STATE_ALARM_TRIGGERED,
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNAVAILABLE,
|
||||||
)
|
)
|
||||||
from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback
|
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
||||||
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity import DeviceInfo
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.event import async_call_later
|
from homeassistant.helpers.event import async_call_later
|
||||||
from homeassistant.helpers.restore_state import RestoreEntity
|
from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
from homeassistant.helpers.typing import StateType
|
from homeassistant.helpers.typing import StateType
|
||||||
|
@ -33,7 +31,6 @@ from .const import (
|
||||||
CONF_PING_INTERVAL,
|
CONF_PING_INTERVAL,
|
||||||
CONF_ZONES,
|
CONF_ZONES,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SIA_ENTITY_ID_FORMAT,
|
|
||||||
SIA_EVENT,
|
SIA_EVENT,
|
||||||
SIA_NAME_FORMAT,
|
SIA_NAME_FORMAT,
|
||||||
SIA_UNIQUE_ID_FORMAT_ALARM,
|
SIA_UNIQUE_ID_FORMAT_ALARM,
|
||||||
|
@ -76,21 +73,17 @@ CODE_CONSEQUENCES: dict[str, StateType] = {
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry: ConfigEntry,
|
entry: ConfigEntry,
|
||||||
async_add_entities: Callable[..., None],
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> bool:
|
) -> None:
|
||||||
"""Set up SIA alarm_control_panel(s) from a config entry."""
|
"""Set up SIA alarm_control_panel(s) from a config entry."""
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
[
|
SIAAlarmControlPanel(entry, account_data, zone)
|
||||||
SIAAlarmControlPanel(entry, account_data, zone)
|
for account_data in entry.data[CONF_ACCOUNTS]
|
||||||
for account_data in entry.data[CONF_ACCOUNTS]
|
for zone in range(
|
||||||
for zone in range(
|
1,
|
||||||
1,
|
entry.options[CONF_ACCOUNTS][account_data[CONF_ACCOUNT]][CONF_ZONES] + 1,
|
||||||
entry.options[CONF_ACCOUNTS][account_data[CONF_ACCOUNT]][CONF_ZONES]
|
)
|
||||||
+ 1,
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class SIAAlarmControlPanel(AlarmControlPanelEntity, RestoreEntity):
|
class SIAAlarmControlPanel(AlarmControlPanelEntity, RestoreEntity):
|
||||||
|
@ -111,18 +104,7 @@ class SIAAlarmControlPanel(AlarmControlPanelEntity, RestoreEntity):
|
||||||
self._account: str = self._account_data[CONF_ACCOUNT]
|
self._account: str = self._account_data[CONF_ACCOUNT]
|
||||||
self._ping_interval: int = self._account_data[CONF_PING_INTERVAL]
|
self._ping_interval: int = self._account_data[CONF_PING_INTERVAL]
|
||||||
|
|
||||||
self.entity_id: str = ALARM_ENTITY_ID_FORMAT.format(
|
self._attr: dict[str, Any] = {}
|
||||||
SIA_ENTITY_ID_FORMAT.format(
|
|
||||||
self._port, self._account, self._zone, DEVICE_CLASS_ALARM
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
self._attr: dict[str, Any] = {
|
|
||||||
CONF_PORT: self._port,
|
|
||||||
CONF_ACCOUNT: self._account,
|
|
||||||
CONF_ZONE: self._zone,
|
|
||||||
CONF_PING_INTERVAL: f"{self._ping_interval} minute(s)",
|
|
||||||
}
|
|
||||||
|
|
||||||
self._available: bool = True
|
self._available: bool = True
|
||||||
self._state: StateType = None
|
self._state: StateType = None
|
||||||
|
@ -134,16 +116,17 @@ class SIAAlarmControlPanel(AlarmControlPanelEntity, RestoreEntity):
|
||||||
|
|
||||||
Overridden from Entity.
|
Overridden from Entity.
|
||||||
|
|
||||||
1. start the event listener and add the callback to on_remove
|
1. register the dispatcher and add the callback to on_remove
|
||||||
2. get previous state from storage
|
2. get previous state from storage
|
||||||
3. if previous state: restore
|
3. if previous state: restore
|
||||||
4. if previous state is unavailable: set _available to False and return
|
4. if previous state is unavailable: set _available to False and return
|
||||||
5. if available: create availability cb
|
5. if available: create availability cb
|
||||||
"""
|
"""
|
||||||
self.async_on_remove(
|
self.async_on_remove(
|
||||||
self.hass.bus.async_listen(
|
async_dispatcher_connect(
|
||||||
event_type=SIA_EVENT.format(self._port, self._account),
|
self.hass,
|
||||||
listener=self.async_handle_event,
|
SIA_EVENT.format(self._port, self._account),
|
||||||
|
self.async_handle_event,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
last_state = await self.async_get_last_state()
|
last_state = await self.async_get_last_state()
|
||||||
|
@ -162,14 +145,11 @@ class SIAAlarmControlPanel(AlarmControlPanelEntity, RestoreEntity):
|
||||||
if self._cancel_availability_cb:
|
if self._cancel_availability_cb:
|
||||||
self._cancel_availability_cb()
|
self._cancel_availability_cb()
|
||||||
|
|
||||||
async def async_handle_event(self, event: Event) -> None:
|
async def async_handle_event(self, sia_event: SIAEvent) -> None:
|
||||||
"""Listen to events for this port and account and update state and attributes.
|
"""Listen to dispatcher events for this port and account and update state and attributes.
|
||||||
|
|
||||||
If the port and account combo receives any message it means it is online and can therefore be set to available.
|
If the port and account combo receives any message it means it is online and can therefore be set to available.
|
||||||
"""
|
"""
|
||||||
sia_event: SIAEvent = SIAEvent.from_dict( # pylint: disable=no-member
|
|
||||||
event.data
|
|
||||||
)
|
|
||||||
_LOGGER.debug("Received event: %s", sia_event)
|
_LOGGER.debug("Received event: %s", sia_event)
|
||||||
if int(sia_event.ri) == self._zone:
|
if int(sia_event.ri) == self._zone:
|
||||||
self._attr.update(get_attr_from_sia_event(sia_event))
|
self._attr.update(get_attr_from_sia_event(sia_event))
|
||||||
|
|
|
@ -18,6 +18,7 @@ import voluptuous as vol
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.const import CONF_PORT, CONF_PROTOCOL
|
from homeassistant.const import CONF_PORT, CONF_PROTOCOL
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.data_entry_flow import FlowResult
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
|
@ -104,7 +105,7 @@ class SIAConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
self._data: ConfigType = {}
|
self._data: ConfigType = {}
|
||||||
self._options: Mapping[str, Any] = {CONF_ACCOUNTS: {}}
|
self._options: Mapping[str, Any] = {CONF_ACCOUNTS: {}}
|
||||||
|
|
||||||
async def async_step_user(self, user_input: ConfigType = None):
|
async def async_step_user(self, user_input: ConfigType = None) -> FlowResult:
|
||||||
"""Handle the initial user step."""
|
"""Handle the initial user step."""
|
||||||
errors: dict[str, str] | None = None
|
errors: dict[str, str] | None = None
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
|
@ -115,7 +116,7 @@ class SIAConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
)
|
)
|
||||||
return await self.async_handle_data_and_route(user_input)
|
return await self.async_handle_data_and_route(user_input)
|
||||||
|
|
||||||
async def async_step_add_account(self, user_input: ConfigType = None):
|
async def async_step_add_account(self, user_input: ConfigType = None) -> FlowResult:
|
||||||
"""Handle the additional accounts steps."""
|
"""Handle the additional accounts steps."""
|
||||||
errors: dict[str, str] | None = None
|
errors: dict[str, str] | None = None
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
|
@ -126,11 +127,11 @@ class SIAConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
)
|
)
|
||||||
return await self.async_handle_data_and_route(user_input)
|
return await self.async_handle_data_and_route(user_input)
|
||||||
|
|
||||||
async def async_handle_data_and_route(self, user_input: ConfigType):
|
async def async_handle_data_and_route(self, user_input: ConfigType) -> FlowResult:
|
||||||
"""Handle the user_input, check if configured and route to the right next step or create entry."""
|
"""Handle the user_input, check if configured and route to the right next step or create entry."""
|
||||||
self._update_data(user_input)
|
self._update_data(user_input)
|
||||||
if self._data and self._port_already_configured():
|
|
||||||
return self.async_abort(reason="already_configured")
|
self._async_abort_entries_match({CONF_PORT: self._data[CONF_PORT]})
|
||||||
|
|
||||||
if user_input[CONF_ADDITIONAL_ACCOUNTS]:
|
if user_input[CONF_ADDITIONAL_ACCOUNTS]:
|
||||||
return await self.async_step_add_account()
|
return await self.async_step_add_account()
|
||||||
|
@ -163,13 +164,6 @@ class SIAConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
self._options[CONF_ACCOUNTS].setdefault(account, deepcopy(DEFAULT_OPTIONS))
|
self._options[CONF_ACCOUNTS].setdefault(account, deepcopy(DEFAULT_OPTIONS))
|
||||||
self._options[CONF_ACCOUNTS][account][CONF_ZONES] = user_input[CONF_ZONES]
|
self._options[CONF_ACCOUNTS][account][CONF_ZONES] = user_input[CONF_ZONES]
|
||||||
|
|
||||||
def _port_already_configured(self):
|
|
||||||
"""See if we already have a SIA entry matching the port."""
|
|
||||||
for entry in self._async_current_entries(include_ignore=False):
|
|
||||||
if entry.data[CONF_PORT] == self._data[CONF_PORT]:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class SIAOptionsFlowHandler(config_entries.OptionsFlow):
|
class SIAOptionsFlowHandler(config_entries.OptionsFlow):
|
||||||
"""Handle SIA options."""
|
"""Handle SIA options."""
|
||||||
|
@ -181,14 +175,15 @@ class SIAOptionsFlowHandler(config_entries.OptionsFlow):
|
||||||
self.hub: SIAHub | None = None
|
self.hub: SIAHub | None = None
|
||||||
self.accounts_todo: list = []
|
self.accounts_todo: list = []
|
||||||
|
|
||||||
async def async_step_init(self, user_input: ConfigType = None):
|
async def async_step_init(self, user_input: ConfigType = None) -> FlowResult:
|
||||||
"""Manage the SIA options."""
|
"""Manage the SIA options."""
|
||||||
self.hub = self.hass.data[DOMAIN][self.config_entry.entry_id]
|
self.hub = self.hass.data[DOMAIN][self.config_entry.entry_id]
|
||||||
if self.hub is not None and self.hub.sia_accounts is not None:
|
assert self.hub is not None
|
||||||
self.accounts_todo = [a.account_id for a in self.hub.sia_accounts]
|
assert self.hub.sia_accounts is not None
|
||||||
return await self.async_step_options()
|
self.accounts_todo = [a.account_id for a in self.hub.sia_accounts]
|
||||||
|
return await self.async_step_options()
|
||||||
|
|
||||||
async def async_step_options(self, user_input: ConfigType = None):
|
async def async_step_options(self, user_input: ConfigType = None) -> FlowResult:
|
||||||
"""Create the options step for a account."""
|
"""Create the options step for a account."""
|
||||||
errors: dict[str, str] | None = None
|
errors: dict[str, str] | None = None
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
|
@ -223,7 +218,6 @@ class SIAOptionsFlowHandler(config_entries.OptionsFlow):
|
||||||
self.options[CONF_ACCOUNTS][account][CONF_ZONES] = user_input[CONF_ZONES]
|
self.options[CONF_ACCOUNTS][account][CONF_ZONES] = user_input[CONF_ZONES]
|
||||||
if self.accounts_todo:
|
if self.accounts_todo:
|
||||||
return await self.async_step_options()
|
return await self.async_step_options()
|
||||||
_LOGGER.warning("Updating SIA Options with %s", self.options)
|
|
||||||
return self.async_create_entry(title="", data=self.options)
|
return self.async_create_entry(title="", data=self.options)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -5,34 +5,24 @@ from homeassistant.components.alarm_control_panel import (
|
||||||
|
|
||||||
PLATFORMS = [ALARM_CONTROL_PANEL_DOMAIN]
|
PLATFORMS = [ALARM_CONTROL_PANEL_DOMAIN]
|
||||||
|
|
||||||
|
DOMAIN = "sia"
|
||||||
|
|
||||||
|
ATTR_CODE = "last_code"
|
||||||
|
ATTR_ZONE = "zone"
|
||||||
|
ATTR_MESSAGE = "last_message"
|
||||||
|
ATTR_ID = "last_id"
|
||||||
|
ATTR_TIMESTAMP = "last_timestamp"
|
||||||
|
|
||||||
|
TITLE = "SIA Alarm on port {}"
|
||||||
CONF_ACCOUNT = "account"
|
CONF_ACCOUNT = "account"
|
||||||
CONF_ACCOUNTS = "accounts"
|
CONF_ACCOUNTS = "accounts"
|
||||||
CONF_ADDITIONAL_ACCOUNTS = "additional_account"
|
CONF_ADDITIONAL_ACCOUNTS = "additional_account"
|
||||||
CONF_PING_INTERVAL = "ping_interval"
|
|
||||||
CONF_ENCRYPTION_KEY = "encryption_key"
|
CONF_ENCRYPTION_KEY = "encryption_key"
|
||||||
CONF_ZONES = "zones"
|
|
||||||
CONF_IGNORE_TIMESTAMPS = "ignore_timestamps"
|
CONF_IGNORE_TIMESTAMPS = "ignore_timestamps"
|
||||||
|
CONF_PING_INTERVAL = "ping_interval"
|
||||||
|
CONF_ZONES = "zones"
|
||||||
|
|
||||||
DOMAIN = "sia"
|
|
||||||
TITLE = "SIA Alarm on port {}"
|
|
||||||
SIA_EVENT = "sia_event_{}_{}"
|
|
||||||
SIA_NAME_FORMAT = "{} - {} - zone {} - {}"
|
SIA_NAME_FORMAT = "{} - {} - zone {} - {}"
|
||||||
SIA_NAME_FORMAT_HUB = "{} - {} - {}"
|
|
||||||
SIA_ENTITY_ID_FORMAT = "{}_{}_{}_{}"
|
|
||||||
SIA_ENTITY_ID_FORMAT_HUB = "{}_{}_{}"
|
|
||||||
SIA_UNIQUE_ID_FORMAT_ALARM = "{}_{}_{}"
|
SIA_UNIQUE_ID_FORMAT_ALARM = "{}_{}_{}"
|
||||||
SIA_UNIQUE_ID_FORMAT = "{}_{}_{}_{}"
|
|
||||||
HUB_SENSOR_NAME = "last_heartbeat"
|
|
||||||
HUB_ZONE = 0
|
|
||||||
PING_INTERVAL_MARGIN = 30
|
|
||||||
|
|
||||||
DEFAULT_TIMEBAND = (80, 40)
|
SIA_EVENT = "sia_event_{}_{}"
|
||||||
IGNORED_TIMEBAND = (3600, 1800)
|
|
||||||
|
|
||||||
EVENT_CODE = "last_code"
|
|
||||||
EVENT_ACCOUNT = "account"
|
|
||||||
EVENT_ZONE = "zone"
|
|
||||||
EVENT_PORT = "port"
|
|
||||||
EVENT_MESSAGE = "last_message"
|
|
||||||
EVENT_ID = "last_id"
|
|
||||||
EVENT_TIMESTAMP = "last_timestamp"
|
|
||||||
|
|
|
@ -9,8 +9,9 @@ from pysiaalarm.aio import CommunicationsProtocol, SIAAccount, SIAClient, SIAEve
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_PORT, CONF_PROTOCOL, EVENT_HOMEASSISTANT_STOP
|
from homeassistant.const import CONF_PORT, CONF_PROTOCOL, EVENT_HOMEASSISTANT_STOP
|
||||||
from homeassistant.core import Event, EventOrigin, HomeAssistant
|
from homeassistant.core import Event, HomeAssistant
|
||||||
from homeassistant.helpers import device_registry as dr
|
from homeassistant.helpers import device_registry as dr
|
||||||
|
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_ACCOUNT,
|
CONF_ACCOUNT,
|
||||||
|
@ -18,16 +19,19 @@ from .const import (
|
||||||
CONF_ENCRYPTION_KEY,
|
CONF_ENCRYPTION_KEY,
|
||||||
CONF_IGNORE_TIMESTAMPS,
|
CONF_IGNORE_TIMESTAMPS,
|
||||||
CONF_ZONES,
|
CONF_ZONES,
|
||||||
DEFAULT_TIMEBAND,
|
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
IGNORED_TIMEBAND,
|
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
SIA_EVENT,
|
SIA_EVENT,
|
||||||
)
|
)
|
||||||
|
from .utils import get_event_data_from_sia_event
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_TIMEBAND = (80, 40)
|
||||||
|
IGNORED_TIMEBAND = (3600, 1800)
|
||||||
|
|
||||||
|
|
||||||
class SIAHub:
|
class SIAHub:
|
||||||
"""Class for SIA Hubs."""
|
"""Class for SIA Hubs."""
|
||||||
|
|
||||||
|
@ -39,7 +43,7 @@ class SIAHub:
|
||||||
"""Create the SIAHub."""
|
"""Create the SIAHub."""
|
||||||
self._hass: HomeAssistant = hass
|
self._hass: HomeAssistant = hass
|
||||||
self._entry: ConfigEntry = entry
|
self._entry: ConfigEntry = entry
|
||||||
self._port: int = int(entry.data[CONF_PORT])
|
self._port: int = entry.data[CONF_PORT]
|
||||||
self._title: str = entry.title
|
self._title: str = entry.title
|
||||||
self._accounts: list[dict[str, Any]] = deepcopy(entry.data[CONF_ACCOUNTS])
|
self._accounts: list[dict[str, Any]] = deepcopy(entry.data[CONF_ACCOUNTS])
|
||||||
self._protocol: str = entry.data[CONF_PROTOCOL]
|
self._protocol: str = entry.data[CONF_PROTOCOL]
|
||||||
|
@ -69,21 +73,23 @@ class SIAHub:
|
||||||
await self.sia_client.stop()
|
await self.sia_client.stop()
|
||||||
|
|
||||||
async def async_create_and_fire_event(self, event: SIAEvent) -> None:
|
async def async_create_and_fire_event(self, event: SIAEvent) -> None:
|
||||||
"""Create a event on HA's bus, with the data from the SIAEvent.
|
"""Create a event on HA dispatcher and then on HA's bus, with the data from the SIAEvent.
|
||||||
|
|
||||||
The created event is handled by default for only a small subset for each platform (there are about 320 SIA Codes defined, only 22 of those are used in the alarm_control_panel), a user can choose to build other automation or even entities on the same event for SIA codes not handled by the built-in platforms.
|
The created event is handled by default for only a small subset for each platform (there are about 320 SIA Codes defined, only 22 of those are used in the alarm_control_panel), a user can choose to build other automation or even entities on the same event for SIA codes not handled by the built-in platforms.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Adding event to bus for code %s for port %s and account %s",
|
"Adding event to dispatch and bus for code %s for port %s and account %s",
|
||||||
event.code,
|
event.code,
|
||||||
self._port,
|
self._port,
|
||||||
event.account,
|
event.account,
|
||||||
)
|
)
|
||||||
|
async_dispatcher_send(
|
||||||
|
self._hass, SIA_EVENT.format(self._port, event.account), event
|
||||||
|
)
|
||||||
self._hass.bus.async_fire(
|
self._hass.bus.async_fire(
|
||||||
event_type=SIA_EVENT.format(self._port, event.account),
|
event_type=SIA_EVENT.format(self._port, event.account),
|
||||||
event_data=event.to_dict(encode_json=True),
|
event_data=get_event_data_from_sia_event(event),
|
||||||
origin=EventOrigin.remote,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def update_accounts(self):
|
def update_accounts(self):
|
||||||
|
@ -115,7 +121,7 @@ class SIAHub:
|
||||||
options = dict(self._entry.options)
|
options = dict(self._entry.options)
|
||||||
for acc in self._accounts:
|
for acc in self._accounts:
|
||||||
acc_id = acc[CONF_ACCOUNT]
|
acc_id = acc[CONF_ACCOUNT]
|
||||||
if acc_id in options[CONF_ACCOUNTS].keys():
|
if acc_id in options[CONF_ACCOUNTS]:
|
||||||
acc[CONF_IGNORE_TIMESTAMPS] = options[CONF_ACCOUNTS][acc_id][
|
acc[CONF_IGNORE_TIMESTAMPS] = options[CONF_ACCOUNTS][acc_id][
|
||||||
CONF_IGNORE_TIMESTAMPS
|
CONF_IGNORE_TIMESTAMPS
|
||||||
]
|
]
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"name": "SIA Alarm Systems",
|
"name": "SIA Alarm Systems",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/sia",
|
"documentation": "https://www.home-assistant.io/integrations/sia",
|
||||||
"requirements": ["pysiaalarm==3.0.0b12"],
|
"requirements": ["pysiaalarm==3.0.0"],
|
||||||
"codeowners": ["@eavanvalkenburg"],
|
"codeowners": ["@eavanvalkenburg"],
|
||||||
"iot_class": "local_push"
|
"iot_class": "local_push"
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"invalid_key_format": "The key is not a hex value, please use only 0-9 and A-F.",
|
"invalid_key_format": "The key is not a hex value, please use only 0-9 and A-F.",
|
||||||
"invalid_key_length": "The key is not the right length, it has to be 16, 24 or 32 characters hex characters.",
|
"invalid_key_length": "The key is not the right length, it has to be 16, 24 or 32 hex characters.",
|
||||||
"invalid_account_format": "The account is not a hex value, please use only 0-9 and A-F.",
|
"invalid_account_format": "The account is not a hex value, please use only 0-9 and A-F.",
|
||||||
"invalid_account_length": "The account is not the right length, it has to be between 3 and 16 characters.",
|
"invalid_account_length": "The account is not the right length, it has to be between 3 and 16 characters.",
|
||||||
"invalid_ping": "The ping interval needs to be between 1 and 1440 minutes.",
|
"invalid_ping": "The ping interval needs to be between 1 and 1440 minutes.",
|
||||||
|
|
|
@ -6,19 +6,9 @@ from typing import Any
|
||||||
|
|
||||||
from pysiaalarm import SIAEvent
|
from pysiaalarm import SIAEvent
|
||||||
|
|
||||||
from homeassistant.const import DEVICE_CLASS_TIMESTAMP
|
from .const import ATTR_CODE, ATTR_ID, ATTR_MESSAGE, ATTR_TIMESTAMP, ATTR_ZONE
|
||||||
|
|
||||||
from .const import (
|
PING_INTERVAL_MARGIN = 30
|
||||||
EVENT_ACCOUNT,
|
|
||||||
EVENT_CODE,
|
|
||||||
EVENT_ID,
|
|
||||||
EVENT_MESSAGE,
|
|
||||||
EVENT_TIMESTAMP,
|
|
||||||
EVENT_ZONE,
|
|
||||||
HUB_SENSOR_NAME,
|
|
||||||
HUB_ZONE,
|
|
||||||
PING_INTERVAL_MARGIN,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_unavailability_interval(ping: int) -> float:
|
def get_unavailability_interval(ping: int) -> float:
|
||||||
|
@ -26,32 +16,55 @@ def get_unavailability_interval(ping: int) -> float:
|
||||||
return timedelta(minutes=ping, seconds=PING_INTERVAL_MARGIN).total_seconds()
|
return timedelta(minutes=ping, seconds=PING_INTERVAL_MARGIN).total_seconds()
|
||||||
|
|
||||||
|
|
||||||
def get_name(port: int, account: str, zone: int, entity_type: str) -> str:
|
|
||||||
"""Give back a entity_id and name according to the variables."""
|
|
||||||
if zone == HUB_ZONE:
|
|
||||||
return f"{port} - {account} - {'Last Heartbeat' if entity_type == DEVICE_CLASS_TIMESTAMP else 'Power'}"
|
|
||||||
return f"{port} - {account} - zone {zone} - {entity_type}"
|
|
||||||
|
|
||||||
|
|
||||||
def get_entity_id(port: int, account: str, zone: int, entity_type: str) -> str:
|
|
||||||
"""Give back a entity_id according to the variables."""
|
|
||||||
if zone == HUB_ZONE:
|
|
||||||
return f"{port}_{account}_{HUB_SENSOR_NAME if entity_type == DEVICE_CLASS_TIMESTAMP else entity_type}"
|
|
||||||
return f"{port}_{account}_{zone}_{entity_type}"
|
|
||||||
|
|
||||||
|
|
||||||
def get_unique_id(entry_id: str, account: str, zone: int, domain: str) -> str:
|
|
||||||
"""Return the unique id."""
|
|
||||||
return f"{entry_id}_{account}_{zone}_{domain}"
|
|
||||||
|
|
||||||
|
|
||||||
def get_attr_from_sia_event(event: SIAEvent) -> dict[str, Any]:
|
def get_attr_from_sia_event(event: SIAEvent) -> dict[str, Any]:
|
||||||
"""Create the attributes dict from a SIAEvent."""
|
"""Create the attributes dict from a SIAEvent."""
|
||||||
return {
|
return {
|
||||||
EVENT_ACCOUNT: event.account,
|
ATTR_ZONE: event.ri,
|
||||||
EVENT_ZONE: event.ri,
|
ATTR_CODE: event.code,
|
||||||
EVENT_CODE: event.code,
|
ATTR_MESSAGE: event.message,
|
||||||
EVENT_MESSAGE: event.message,
|
ATTR_ID: event.id,
|
||||||
EVENT_ID: event.id,
|
ATTR_TIMESTAMP: event.timestamp.isoformat(),
|
||||||
EVENT_TIMESTAMP: event.timestamp,
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_event_data_from_sia_event(event: SIAEvent) -> dict[str, Any]:
|
||||||
|
"""Create a dict from the SIA Event for the HA Event."""
|
||||||
|
return {
|
||||||
|
"message_type": event.message_type,
|
||||||
|
"receiver": event.receiver,
|
||||||
|
"line": event.line,
|
||||||
|
"account": event.account,
|
||||||
|
"sequence": event.sequence,
|
||||||
|
"content": event.content,
|
||||||
|
"ti": event.ti,
|
||||||
|
"id": event.id,
|
||||||
|
"ri": event.ri,
|
||||||
|
"code": event.code,
|
||||||
|
"message": event.message,
|
||||||
|
"x_data": event.x_data,
|
||||||
|
"timestamp": event.timestamp.isoformat(),
|
||||||
|
"event_qualifier": event.qualifier,
|
||||||
|
"event_type": event.event_type,
|
||||||
|
"partition": event.partition,
|
||||||
|
"extended_data": [
|
||||||
|
{
|
||||||
|
"identifier": xd.identifier,
|
||||||
|
"name": xd.name,
|
||||||
|
"description": xd.description,
|
||||||
|
"length": xd.length,
|
||||||
|
"characters": xd.characters,
|
||||||
|
"value": xd.value,
|
||||||
|
}
|
||||||
|
for xd in event.extended_data
|
||||||
|
]
|
||||||
|
if event.extended_data is not None
|
||||||
|
else None,
|
||||||
|
"sia_code": {
|
||||||
|
"code": event.sia_code.code,
|
||||||
|
"type": event.sia_code.type,
|
||||||
|
"description": event.sia_code.description,
|
||||||
|
"concerns": event.sia_code.concerns,
|
||||||
|
}
|
||||||
|
if event.sia_code is not None
|
||||||
|
else None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1723,7 +1723,7 @@ pysesame2==1.0.1
|
||||||
pysher==1.0.1
|
pysher==1.0.1
|
||||||
|
|
||||||
# homeassistant.components.sia
|
# homeassistant.components.sia
|
||||||
pysiaalarm==3.0.0b12
|
pysiaalarm==3.0.0
|
||||||
|
|
||||||
# homeassistant.components.signal_messenger
|
# homeassistant.components.signal_messenger
|
||||||
pysignalclirestapi==0.3.4
|
pysignalclirestapi==0.3.4
|
||||||
|
|
|
@ -962,7 +962,7 @@ pyserial-asyncio==0.5
|
||||||
pyserial==3.5
|
pyserial==3.5
|
||||||
|
|
||||||
# homeassistant.components.sia
|
# homeassistant.components.sia
|
||||||
pysiaalarm==3.0.0b12
|
pysiaalarm==3.0.0
|
||||||
|
|
||||||
# homeassistant.components.signal_messenger
|
# homeassistant.components.signal_messenger
|
||||||
pysignalclirestapi==0.3.4
|
pysignalclirestapi==0.3.4
|
||||||
|
|
Loading…
Add table
Reference in a new issue