mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 21:57:51 +00:00
Add binary sensor platform to SIA integration (#51206)
* add support for binary_sensor * added default enabled for binary sensors * fixed coverage and a import deleted * disable pylint for line * Apply suggestions from code review * split binary sensor and used more attr fields Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
parent
55f158cf78
commit
e8762bdea1
@ -920,9 +920,11 @@ omit =
|
|||||||
homeassistant/components/slack/notify.py
|
homeassistant/components/slack/notify.py
|
||||||
homeassistant/components/sia/__init__.py
|
homeassistant/components/sia/__init__.py
|
||||||
homeassistant/components/sia/alarm_control_panel.py
|
homeassistant/components/sia/alarm_control_panel.py
|
||||||
|
homeassistant/components/sia/binary_sensor.py
|
||||||
homeassistant/components/sia/const.py
|
homeassistant/components/sia/const.py
|
||||||
homeassistant/components/sia/hub.py
|
homeassistant/components/sia/hub.py
|
||||||
homeassistant/components/sia/utils.py
|
homeassistant/components/sia/utils.py
|
||||||
|
homeassistant/components/sia/sia_entity_base.py
|
||||||
homeassistant/components/sinch/*
|
homeassistant/components/sinch/*
|
||||||
homeassistant/components/slide/*
|
homeassistant/components/slide/*
|
||||||
homeassistant/components/sma/__init__.py
|
homeassistant/components/sma/__init__.py
|
||||||
|
@ -9,7 +9,6 @@ from pysiaalarm import SIAEvent
|
|||||||
from homeassistant.components.alarm_control_panel import AlarmControlPanelEntity
|
from homeassistant.components.alarm_control_panel import AlarmControlPanelEntity
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_PORT,
|
|
||||||
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,
|
||||||
@ -17,25 +16,12 @@ from homeassistant.const import (
|
|||||||
STATE_ALARM_TRIGGERED,
|
STATE_ALARM_TRIGGERED,
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNAVAILABLE,
|
||||||
)
|
)
|
||||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, State
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
|
||||||
from homeassistant.helpers.entity import DeviceInfo
|
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.event import async_call_later
|
|
||||||
from homeassistant.helpers.restore_state import RestoreEntity
|
|
||||||
from homeassistant.helpers.typing import StateType
|
from homeassistant.helpers.typing import StateType
|
||||||
|
|
||||||
from .const import (
|
from .const import CONF_ACCOUNT, CONF_ACCOUNTS, CONF_ZONES, SIA_UNIQUE_ID_FORMAT_ALARM
|
||||||
CONF_ACCOUNT,
|
from .sia_entity_base import SIABaseEntity
|
||||||
CONF_ACCOUNTS,
|
|
||||||
CONF_PING_INTERVAL,
|
|
||||||
CONF_ZONES,
|
|
||||||
DOMAIN,
|
|
||||||
SIA_EVENT,
|
|
||||||
SIA_NAME_FORMAT,
|
|
||||||
SIA_UNIQUE_ID_FORMAT_ALARM,
|
|
||||||
)
|
|
||||||
from .utils import get_attr_from_sia_event, get_unavailability_interval
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -86,7 +72,7 @@ async def async_setup_entry(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SIAAlarmControlPanel(AlarmControlPanelEntity, RestoreEntity):
|
class SIAAlarmControlPanel(AlarmControlPanelEntity, SIABaseEntity):
|
||||||
"""Class for SIA Alarm Control Panels."""
|
"""Class for SIA Alarm Control Panels."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -96,138 +82,31 @@ class SIAAlarmControlPanel(AlarmControlPanelEntity, RestoreEntity):
|
|||||||
zone: int,
|
zone: int,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Create SIAAlarmControlPanel object."""
|
"""Create SIAAlarmControlPanel object."""
|
||||||
self._entry: ConfigEntry = entry
|
super().__init__(entry, account_data, zone, DEVICE_CLASS_ALARM)
|
||||||
self._account_data: dict[str, Any] = account_data
|
self._attr_state: StateType = None
|
||||||
self._zone: int = zone
|
|
||||||
|
|
||||||
self._port: int = self._entry.data[CONF_PORT]
|
|
||||||
self._account: str = self._account_data[CONF_ACCOUNT]
|
|
||||||
self._ping_interval: int = self._account_data[CONF_PING_INTERVAL]
|
|
||||||
|
|
||||||
self._attr: dict[str, Any] = {}
|
|
||||||
|
|
||||||
self._available: bool = True
|
|
||||||
self._state: StateType = None
|
|
||||||
self._old_state: StateType = None
|
self._old_state: StateType = None
|
||||||
self._cancel_availability_cb: CALLBACK_TYPE | None = None
|
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
self._attr_unique_id = SIA_UNIQUE_ID_FORMAT_ALARM.format(
|
||||||
"""Run when entity about to be added to hass.
|
|
||||||
|
|
||||||
Overridden from Entity.
|
|
||||||
|
|
||||||
1. register the dispatcher and add the callback to on_remove
|
|
||||||
2. get previous state from storage
|
|
||||||
3. if previous state: restore
|
|
||||||
4. if previous state is unavailable: set _available to False and return
|
|
||||||
5. if available: create availability cb
|
|
||||||
"""
|
|
||||||
self.async_on_remove(
|
|
||||||
async_dispatcher_connect(
|
|
||||||
self.hass,
|
|
||||||
SIA_EVENT.format(self._port, self._account),
|
|
||||||
self.async_handle_event,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
last_state = await self.async_get_last_state()
|
|
||||||
if last_state is not None:
|
|
||||||
self._state = last_state.state
|
|
||||||
if self.state == STATE_UNAVAILABLE:
|
|
||||||
self._available = False
|
|
||||||
return
|
|
||||||
self._cancel_availability_cb = self.async_create_availability_cb()
|
|
||||||
|
|
||||||
async def async_will_remove_from_hass(self) -> None:
|
|
||||||
"""Run when entity will be removed from hass.
|
|
||||||
|
|
||||||
Overridden from Entity.
|
|
||||||
"""
|
|
||||||
if self._cancel_availability_cb:
|
|
||||||
self._cancel_availability_cb()
|
|
||||||
|
|
||||||
async def async_handle_event(self, sia_event: SIAEvent) -> None:
|
|
||||||
"""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.
|
|
||||||
"""
|
|
||||||
_LOGGER.debug("Received event: %s", sia_event)
|
|
||||||
if int(sia_event.ri) == self._zone:
|
|
||||||
self._attr.update(get_attr_from_sia_event(sia_event))
|
|
||||||
new_state = CODE_CONSEQUENCES.get(sia_event.code, None)
|
|
||||||
if new_state is not None:
|
|
||||||
if new_state == PREVIOUS_STATE:
|
|
||||||
new_state = self._old_state
|
|
||||||
self._state, self._old_state = new_state, self._state
|
|
||||||
self._available = True
|
|
||||||
self.async_write_ha_state()
|
|
||||||
self.async_reset_availability_cb()
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_reset_availability_cb(self) -> None:
|
|
||||||
"""Reset availability cb by cancelling the current and creating a new one."""
|
|
||||||
if self._cancel_availability_cb:
|
|
||||||
self._cancel_availability_cb()
|
|
||||||
self._cancel_availability_cb = self.async_create_availability_cb()
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_create_availability_cb(self) -> CALLBACK_TYPE:
|
|
||||||
"""Create a availability cb and return the callback."""
|
|
||||||
return async_call_later(
|
|
||||||
self.hass,
|
|
||||||
get_unavailability_interval(self._ping_interval),
|
|
||||||
self.async_set_unavailable,
|
|
||||||
)
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_set_unavailable(self, _) -> None:
|
|
||||||
"""Set unavailable."""
|
|
||||||
self._available = False
|
|
||||||
self.async_write_ha_state()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def state(self) -> StateType:
|
|
||||||
"""Get state."""
|
|
||||||
return self._state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
"""Get Name."""
|
|
||||||
return SIA_NAME_FORMAT.format(
|
|
||||||
self._port, self._account, self._zone, DEVICE_CLASS_ALARM
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def unique_id(self) -> str:
|
|
||||||
"""Get unique_id."""
|
|
||||||
return SIA_UNIQUE_ID_FORMAT_ALARM.format(
|
|
||||||
self._entry.entry_id, self._account, self._zone
|
self._entry.entry_id, self._account, self._zone
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
def update_state(self, sia_event: SIAEvent) -> None:
|
||||||
def available(self) -> bool:
|
"""Update the state of the alarm control panel."""
|
||||||
"""Get availability."""
|
new_state = CODE_CONSEQUENCES.get(sia_event.code, None)
|
||||||
return self._available
|
if new_state is not None:
|
||||||
|
_LOGGER.debug("New state will be %s", new_state)
|
||||||
|
if new_state == PREVIOUS_STATE:
|
||||||
|
new_state = self._old_state
|
||||||
|
self._attr_state, self._old_state = new_state, self._attr_state
|
||||||
|
|
||||||
@property
|
def handle_last_state(self, last_state: State | None) -> None:
|
||||||
def extra_state_attributes(self) -> dict[str, Any]:
|
"""Handle the last state."""
|
||||||
"""Return device attributes."""
|
if last_state is not None:
|
||||||
return self._attr
|
self._attr_state = last_state.state
|
||||||
|
if self.state == STATE_UNAVAILABLE:
|
||||||
@property
|
self._attr_available = False
|
||||||
def should_poll(self) -> bool:
|
|
||||||
"""Return False if entity pushes its state to HA."""
|
|
||||||
return False
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_features(self) -> int:
|
def supported_features(self) -> int:
|
||||||
"""Flag supported features."""
|
"""Return the list of supported features."""
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@property
|
|
||||||
def device_info(self) -> DeviceInfo:
|
|
||||||
"""Return the device_info."""
|
|
||||||
return {
|
|
||||||
"identifiers": {(DOMAIN, self.unique_id)},
|
|
||||||
"name": self.name,
|
|
||||||
"via_device": (DOMAIN, f"{self._port}_{self._account}"),
|
|
||||||
}
|
|
||||||
|
163
homeassistant/components/sia/binary_sensor.py
Normal file
163
homeassistant/components/sia/binary_sensor.py
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
"""Module for SIA Binary Sensors."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Iterable
|
||||||
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from pysiaalarm import SIAEvent
|
||||||
|
|
||||||
|
from homeassistant.components.binary_sensor import (
|
||||||
|
DEVICE_CLASS_MOISTURE,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
|
DEVICE_CLASS_SMOKE,
|
||||||
|
BinarySensorEntity,
|
||||||
|
)
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE
|
||||||
|
from homeassistant.core import HomeAssistant, State
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
|
from .const import (
|
||||||
|
CONF_ACCOUNT,
|
||||||
|
CONF_ACCOUNTS,
|
||||||
|
CONF_ZONES,
|
||||||
|
SIA_HUB_ZONE,
|
||||||
|
SIA_UNIQUE_ID_FORMAT_BINARY,
|
||||||
|
)
|
||||||
|
from .sia_entity_base import SIABaseEntity
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
POWER_CODE_CONSEQUENCES: dict[str, bool] = {
|
||||||
|
"AT": False,
|
||||||
|
"AR": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
SMOKE_CODE_CONSEQUENCES: dict[str, bool] = {
|
||||||
|
"GA": True,
|
||||||
|
"GH": False,
|
||||||
|
"FA": True,
|
||||||
|
"FH": False,
|
||||||
|
"KA": True,
|
||||||
|
"KH": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
MOISTURE_CODE_CONSEQUENCES: dict[str, bool] = {
|
||||||
|
"WA": True,
|
||||||
|
"WH": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def generate_binary_sensors(entry) -> Iterable[SIABinarySensorBase]:
|
||||||
|
"""Generate binary sensors.
|
||||||
|
|
||||||
|
For each Account there is one power sensor with zone == 0.
|
||||||
|
For each Zone in each Account there is one smoke and one moisture sensor.
|
||||||
|
"""
|
||||||
|
for account in entry.data[CONF_ACCOUNTS]:
|
||||||
|
yield SIABinarySensorPower(entry, account)
|
||||||
|
zones = entry.options[CONF_ACCOUNTS][account[CONF_ACCOUNT]][CONF_ZONES]
|
||||||
|
for zone in range(1, zones + 1):
|
||||||
|
yield SIABinarySensorSmoke(entry, account, zone)
|
||||||
|
yield SIABinarySensorMoisture(entry, account, zone)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up SIA binary sensors from a config entry."""
|
||||||
|
async_add_entities(generate_binary_sensors(entry))
|
||||||
|
|
||||||
|
|
||||||
|
class SIABinarySensorBase(BinarySensorEntity, SIABaseEntity):
|
||||||
|
"""Class for SIA Binary Sensors."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
entry: ConfigEntry,
|
||||||
|
account_data: dict[str, Any],
|
||||||
|
zone: int,
|
||||||
|
device_class: str,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize a base binary sensor."""
|
||||||
|
super().__init__(entry, account_data, zone, device_class)
|
||||||
|
|
||||||
|
self._attr_unique_id = SIA_UNIQUE_ID_FORMAT_BINARY.format(
|
||||||
|
self._entry.entry_id, self._account, self._zone, self._attr_device_class
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle_last_state(self, last_state: State | None) -> None:
|
||||||
|
"""Handle the last state."""
|
||||||
|
if last_state is not None and last_state.state is not None:
|
||||||
|
if last_state.state == STATE_ON:
|
||||||
|
self._attr_is_on = True
|
||||||
|
elif last_state.state == STATE_OFF:
|
||||||
|
self._attr_is_on = False
|
||||||
|
elif last_state.state == STATE_UNAVAILABLE:
|
||||||
|
self._attr_available = False
|
||||||
|
|
||||||
|
|
||||||
|
class SIABinarySensorMoisture(SIABinarySensorBase):
|
||||||
|
"""Class for Moisture Binary Sensors."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
entry: ConfigEntry,
|
||||||
|
account_data: dict[str, Any],
|
||||||
|
zone: int,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize a Moisture binary sensor."""
|
||||||
|
super().__init__(entry, account_data, zone, DEVICE_CLASS_MOISTURE)
|
||||||
|
self._attr_entity_registry_enabled_default = False
|
||||||
|
|
||||||
|
def update_state(self, sia_event: SIAEvent) -> None:
|
||||||
|
"""Update the state of the binary sensor."""
|
||||||
|
new_state = MOISTURE_CODE_CONSEQUENCES.get(sia_event.code, None)
|
||||||
|
if new_state is not None:
|
||||||
|
_LOGGER.debug("New state will be %s", new_state)
|
||||||
|
self._attr_is_on = new_state
|
||||||
|
|
||||||
|
|
||||||
|
class SIABinarySensorSmoke(SIABinarySensorBase):
|
||||||
|
"""Class for Smoke Binary Sensors."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
entry: ConfigEntry,
|
||||||
|
account_data: dict[str, Any],
|
||||||
|
zone: int,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize a Smoke binary sensor."""
|
||||||
|
super().__init__(entry, account_data, zone, DEVICE_CLASS_SMOKE)
|
||||||
|
self._attr_entity_registry_enabled_default = False
|
||||||
|
|
||||||
|
def update_state(self, sia_event: SIAEvent) -> None:
|
||||||
|
"""Update the state of the binary sensor."""
|
||||||
|
new_state = SMOKE_CODE_CONSEQUENCES.get(sia_event.code, None)
|
||||||
|
if new_state is not None:
|
||||||
|
_LOGGER.debug("New state will be %s", new_state)
|
||||||
|
self._attr_is_on = new_state
|
||||||
|
|
||||||
|
|
||||||
|
class SIABinarySensorPower(SIABinarySensorBase):
|
||||||
|
"""Class for Power Binary Sensors."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
entry: ConfigEntry,
|
||||||
|
account_data: dict[str, Any],
|
||||||
|
) -> None:
|
||||||
|
"""Initialize a Power binary sensor."""
|
||||||
|
super().__init__(entry, account_data, SIA_HUB_ZONE, DEVICE_CLASS_POWER)
|
||||||
|
self._attr_entity_registry_enabled_default = True
|
||||||
|
|
||||||
|
def update_state(self, sia_event: SIAEvent) -> None:
|
||||||
|
"""Update the state of the binary sensor."""
|
||||||
|
new_state = POWER_CODE_CONSEQUENCES.get(sia_event.code, None)
|
||||||
|
if new_state is not None:
|
||||||
|
_LOGGER.debug("New state will be %s", new_state)
|
||||||
|
self._attr_is_on = new_state
|
@ -2,13 +2,14 @@
|
|||||||
from homeassistant.components.alarm_control_panel import (
|
from homeassistant.components.alarm_control_panel import (
|
||||||
DOMAIN as ALARM_CONTROL_PANEL_DOMAIN,
|
DOMAIN as ALARM_CONTROL_PANEL_DOMAIN,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
||||||
|
|
||||||
PLATFORMS = [ALARM_CONTROL_PANEL_DOMAIN]
|
PLATFORMS = [ALARM_CONTROL_PANEL_DOMAIN, BINARY_SENSOR_DOMAIN]
|
||||||
|
|
||||||
DOMAIN = "sia"
|
DOMAIN = "sia"
|
||||||
|
|
||||||
ATTR_CODE = "last_code"
|
ATTR_CODE = "last_code"
|
||||||
ATTR_ZONE = "zone"
|
ATTR_ZONE = "last_zone"
|
||||||
ATTR_MESSAGE = "last_message"
|
ATTR_MESSAGE = "last_message"
|
||||||
ATTR_ID = "last_id"
|
ATTR_ID = "last_id"
|
||||||
ATTR_TIMESTAMP = "last_timestamp"
|
ATTR_TIMESTAMP = "last_timestamp"
|
||||||
@ -24,5 +25,7 @@ CONF_ZONES = "zones"
|
|||||||
|
|
||||||
SIA_NAME_FORMAT = "{} - {} - zone {} - {}"
|
SIA_NAME_FORMAT = "{} - {} - zone {} - {}"
|
||||||
SIA_UNIQUE_ID_FORMAT_ALARM = "{}_{}_{}"
|
SIA_UNIQUE_ID_FORMAT_ALARM = "{}_{}_{}"
|
||||||
|
SIA_UNIQUE_ID_FORMAT_BINARY = "{}_{}_{}_{}"
|
||||||
|
SIA_HUB_ZONE = 0
|
||||||
|
|
||||||
SIA_EVENT = "sia_event_{}_{}"
|
SIA_EVENT = "sia_event_{}_{}"
|
||||||
|
131
homeassistant/components/sia/sia_entity_base.py
Normal file
131
homeassistant/components/sia/sia_entity_base.py
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
"""Module for SIA Base Entity."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from abc import abstractmethod
|
||||||
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from pysiaalarm import SIAEvent
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.const import CONF_PORT
|
||||||
|
from homeassistant.core import CALLBACK_TYPE, State, callback
|
||||||
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
|
from homeassistant.helpers.event import async_call_later
|
||||||
|
from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
|
|
||||||
|
from .const import CONF_ACCOUNT, CONF_PING_INTERVAL, DOMAIN, SIA_EVENT, SIA_NAME_FORMAT
|
||||||
|
from .utils import get_attr_from_sia_event, get_unavailability_interval
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class SIABaseEntity(RestoreEntity):
|
||||||
|
"""Base class for SIA entities."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
entry: ConfigEntry,
|
||||||
|
account_data: dict[str, Any],
|
||||||
|
zone: int,
|
||||||
|
device_class: str,
|
||||||
|
) -> None:
|
||||||
|
"""Create SIABaseEntity object."""
|
||||||
|
self._entry: ConfigEntry = entry
|
||||||
|
self._account_data: dict[str, Any] = account_data
|
||||||
|
self._zone: int = zone
|
||||||
|
self._attr_device_class: str = device_class
|
||||||
|
|
||||||
|
self._port: int = self._entry.data[CONF_PORT]
|
||||||
|
self._account: str = self._account_data[CONF_ACCOUNT]
|
||||||
|
self._ping_interval: int = self._account_data[CONF_PING_INTERVAL]
|
||||||
|
|
||||||
|
self._cancel_availability_cb: CALLBACK_TYPE | None = None
|
||||||
|
|
||||||
|
self._attr_extra_state_attributes: dict[str, Any] = {}
|
||||||
|
self._attr_should_poll = False
|
||||||
|
self._attr_name = SIA_NAME_FORMAT.format(
|
||||||
|
self._port, self._account, self._zone, self._attr_device_class
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
"""Run when entity about to be added to hass.
|
||||||
|
|
||||||
|
Overridden from Entity.
|
||||||
|
|
||||||
|
1. register the dispatcher and add the callback to on_remove
|
||||||
|
2. get previous state from storage and pass to entity specific function
|
||||||
|
3. if available: create availability cb
|
||||||
|
"""
|
||||||
|
self.async_on_remove(
|
||||||
|
async_dispatcher_connect(
|
||||||
|
self.hass,
|
||||||
|
SIA_EVENT.format(self._port, self._account),
|
||||||
|
self.async_handle_event,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.handle_last_state(await self.async_get_last_state())
|
||||||
|
if self._attr_available:
|
||||||
|
self.async_create_availability_cb()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def handle_last_state(self, last_state: State | None) -> None:
|
||||||
|
"""Handle the last state."""
|
||||||
|
|
||||||
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
|
"""Run when entity will be removed from hass.
|
||||||
|
|
||||||
|
Overridden from Entity.
|
||||||
|
"""
|
||||||
|
if self._cancel_availability_cb:
|
||||||
|
self._cancel_availability_cb()
|
||||||
|
|
||||||
|
async def async_handle_event(self, sia_event: SIAEvent) -> None:
|
||||||
|
"""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.
|
||||||
|
"""
|
||||||
|
_LOGGER.debug("Received event: %s", sia_event)
|
||||||
|
if int(sia_event.ri) == self._zone:
|
||||||
|
self._attr_extra_state_attributes.update(get_attr_from_sia_event(sia_event))
|
||||||
|
self.update_state(sia_event)
|
||||||
|
self.async_reset_availability_cb()
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def update_state(self, sia_event: SIAEvent) -> None:
|
||||||
|
"""Do the entity specific state updates."""
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_reset_availability_cb(self) -> None:
|
||||||
|
"""Reset availability cb by cancelling the current and creating a new one."""
|
||||||
|
self._attr_available = True
|
||||||
|
if self._cancel_availability_cb:
|
||||||
|
self._cancel_availability_cb()
|
||||||
|
self.async_create_availability_cb()
|
||||||
|
|
||||||
|
def async_create_availability_cb(self) -> None:
|
||||||
|
"""Create a availability cb and return the callback."""
|
||||||
|
self._cancel_availability_cb = async_call_later(
|
||||||
|
self.hass,
|
||||||
|
get_unavailability_interval(self._ping_interval),
|
||||||
|
self.async_set_unavailable,
|
||||||
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_set_unavailable(self, _) -> None:
|
||||||
|
"""Set unavailable."""
|
||||||
|
self._attr_available = False
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_info(self) -> DeviceInfo:
|
||||||
|
"""Return the device_info."""
|
||||||
|
assert self._attr_name is not None
|
||||||
|
assert self.unique_id is not None
|
||||||
|
return {
|
||||||
|
"name": self._attr_name,
|
||||||
|
"identifiers": {(DOMAIN, self.unique_id)},
|
||||||
|
"via_device": (DOMAIN, f"{self._port}_{self._account}"),
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user