Implement entity_descriptions in SIA (#63130)

* moved sia to entity_descriptions

* cleanup

* moved logger to const

* redid entity description to static

* small fix

* moved entity description classes to proper file

* improved entity def's and undid logger

* further cleanup

* redid naming logic

* Clean up

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Eduard van Valkenburg 2022-01-17 10:50:53 +01:00 committed by GitHub
parent 8459a28489
commit b66fd820ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 296 additions and 222 deletions

View file

@ -1,14 +1,18 @@
"""Module for SIA Alarm Control Panels."""
from __future__ import annotations
from dataclasses import dataclass
import logging
from typing import Any
from pysiaalarm import SIAEvent
from homeassistant.components.alarm_control_panel import AlarmControlPanelEntity
from homeassistant.components.alarm_control_panel import (
AlarmControlPanelEntity,
AlarmControlPanelEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_PORT,
STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_CUSTOM_BYPASS,
STATE_ALARM_ARMED_NIGHT,
@ -20,40 +24,58 @@ from homeassistant.core import HomeAssistant, State
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from .const import CONF_ACCOUNT, CONF_ACCOUNTS, CONF_ZONES, SIA_UNIQUE_ID_FORMAT_ALARM
from .sia_entity_base import SIABaseEntity
from .const import (
CONF_ACCOUNT,
CONF_ACCOUNTS,
CONF_PING_INTERVAL,
CONF_ZONES,
KEY_ALARM,
PREVIOUS_STATE,
SIA_NAME_FORMAT,
SIA_UNIQUE_ID_FORMAT_ALARM,
)
from .sia_entity_base import SIABaseEntity, SIAEntityDescription
_LOGGER = logging.getLogger(__name__)
DEVICE_CLASS_ALARM = "alarm"
PREVIOUS_STATE = "previous_state"
CODE_CONSEQUENCES: dict[str, StateType] = {
"PA": STATE_ALARM_TRIGGERED,
"JA": STATE_ALARM_TRIGGERED,
"TA": STATE_ALARM_TRIGGERED,
"BA": STATE_ALARM_TRIGGERED,
"CA": STATE_ALARM_ARMED_AWAY,
"CB": STATE_ALARM_ARMED_AWAY,
"CG": STATE_ALARM_ARMED_AWAY,
"CL": STATE_ALARM_ARMED_AWAY,
"CP": STATE_ALARM_ARMED_AWAY,
"CQ": STATE_ALARM_ARMED_AWAY,
"CS": STATE_ALARM_ARMED_AWAY,
"CF": STATE_ALARM_ARMED_CUSTOM_BYPASS,
"OA": STATE_ALARM_DISARMED,
"OB": STATE_ALARM_DISARMED,
"OG": STATE_ALARM_DISARMED,
"OP": STATE_ALARM_DISARMED,
"OQ": STATE_ALARM_DISARMED,
"OR": STATE_ALARM_DISARMED,
"OS": STATE_ALARM_DISARMED,
"NC": STATE_ALARM_ARMED_NIGHT,
"NL": STATE_ALARM_ARMED_NIGHT,
"BR": PREVIOUS_STATE,
"NP": PREVIOUS_STATE,
"NO": PREVIOUS_STATE,
}
@dataclass
class SIAAlarmControlPanelEntityDescription(
AlarmControlPanelEntityDescription,
SIAEntityDescription,
):
"""Describes SIA alarm control panel entity."""
ENTITY_DESCRIPTION_ALARM = SIAAlarmControlPanelEntityDescription(
key=KEY_ALARM,
code_consequences={
"PA": STATE_ALARM_TRIGGERED,
"JA": STATE_ALARM_TRIGGERED,
"TA": STATE_ALARM_TRIGGERED,
"BA": STATE_ALARM_TRIGGERED,
"CA": STATE_ALARM_ARMED_AWAY,
"CB": STATE_ALARM_ARMED_AWAY,
"CG": STATE_ALARM_ARMED_AWAY,
"CL": STATE_ALARM_ARMED_AWAY,
"CP": STATE_ALARM_ARMED_AWAY,
"CQ": STATE_ALARM_ARMED_AWAY,
"CS": STATE_ALARM_ARMED_AWAY,
"CF": STATE_ALARM_ARMED_CUSTOM_BYPASS,
"OA": STATE_ALARM_DISARMED,
"OB": STATE_ALARM_DISARMED,
"OG": STATE_ALARM_DISARMED,
"OP": STATE_ALARM_DISARMED,
"OQ": STATE_ALARM_DISARMED,
"OR": STATE_ALARM_DISARMED,
"OS": STATE_ALARM_DISARMED,
"NC": STATE_ALARM_ARMED_NIGHT,
"NL": STATE_ALARM_ARMED_NIGHT,
"BR": PREVIOUS_STATE,
"NP": PREVIOUS_STATE,
"NO": PREVIOUS_STATE,
},
)
async def async_setup_entry(
@ -63,7 +85,19 @@ async def async_setup_entry(
) -> None:
"""Set up SIA alarm_control_panel(s) from a config entry."""
async_add_entities(
SIAAlarmControlPanel(entry, account_data, zone)
SIAAlarmControlPanel(
port=entry.data[CONF_PORT],
account=account_data[CONF_ACCOUNT],
zone=zone,
ping_interval=account_data[CONF_PING_INTERVAL],
entity_description=ENTITY_DESCRIPTION_ALARM,
unique_id=SIA_UNIQUE_ID_FORMAT_ALARM.format(
entry.entry_id, account_data[CONF_ACCOUNT], zone
),
name=SIA_NAME_FORMAT.format(
entry.data[CONF_PORT], account_data[CONF_ACCOUNT], zone, "alarm"
),
)
for account_data in entry.data[CONF_ACCOUNTS]
for zone in range(
1,
@ -75,29 +109,32 @@ async def async_setup_entry(
class SIAAlarmControlPanel(SIABaseEntity, AlarmControlPanelEntity):
"""Class for SIA Alarm Control Panels."""
entity_description: SIAAlarmControlPanelEntityDescription
_attr_supported_features = 0
def __init__(
self,
entry: ConfigEntry,
account_data: dict[str, Any],
zone: int,
port: int,
account: str,
zone: int | None,
ping_interval: int,
entity_description: SIAAlarmControlPanelEntityDescription,
unique_id: str,
name: str,
) -> None:
"""Create SIAAlarmControlPanel object."""
super().__init__(entry, account_data, zone, DEVICE_CLASS_ALARM)
self._attr_state: StateType = None
self._old_state: StateType = None
self._attr_unique_id = SIA_UNIQUE_ID_FORMAT_ALARM.format(
self._entry.entry_id, self._account, self._zone
super().__init__(
port,
account,
zone,
ping_interval,
entity_description,
unique_id,
name,
)
def update_state(self, sia_event: SIAEvent) -> None:
"""Update the state of the alarm control panel."""
new_state = CODE_CONSEQUENCES.get(sia_event.code, None)
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
self._attr_state: StateType = None
self._old_state: StateType = None
def handle_last_state(self, last_state: State | None) -> None:
"""Handle the last state."""
@ -106,7 +143,13 @@ class SIAAlarmControlPanel(SIABaseEntity, AlarmControlPanelEntity):
if self.state == STATE_UNAVAILABLE:
self._attr_available = False
@property
def supported_features(self) -> int:
"""Return the list of supported features."""
return 0
def update_state(self, sia_event: SIAEvent) -> bool:
"""Update the state of the alarm control panel."""
new_state = self.entity_description.code_consequences.get(sia_event.code)
if new_state is None:
return False
_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
return True

View file

@ -2,64 +2,148 @@
from __future__ import annotations
from collections.abc import Iterable
from dataclasses import dataclass
import logging
from typing import Any
from pysiaalarm import SIAEvent
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE
from homeassistant.const import CONF_PORT, STATE_OFF, STATE_ON, STATE_UNAVAILABLE
from homeassistant.core import HomeAssistant, State
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import (
CONF_ACCOUNT,
CONF_ACCOUNTS,
CONF_PING_INTERVAL,
CONF_ZONES,
KEY_MOISTURE,
KEY_POWER,
KEY_SMOKE,
SIA_HUB_ZONE,
SIA_NAME_FORMAT,
SIA_NAME_FORMAT_HUB,
SIA_UNIQUE_ID_FORMAT_BINARY,
)
from .sia_entity_base import SIABaseEntity
from .sia_entity_base import SIABaseEntity, SIAEntityDescription
_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,
}
@dataclass
class SIABinarySensorEntityDescription(
BinarySensorEntityDescription,
SIAEntityDescription,
):
"""Describes SIA sensor entity."""
def generate_binary_sensors(entry) -> Iterable[SIABinarySensorBase]:
ENTITY_DESCRIPTION_POWER = SIABinarySensorEntityDescription(
key=KEY_POWER,
device_class=BinarySensorDeviceClass.POWER,
entity_category=EntityCategory.DIAGNOSTIC,
code_consequences={
"AT": False,
"AR": True,
},
)
ENTITY_DESCRIPTION_SMOKE = SIABinarySensorEntityDescription(
key=KEY_SMOKE,
device_class=BinarySensorDeviceClass.SMOKE,
code_consequences={
"GA": True,
"GH": False,
"FA": True,
"FH": False,
"KA": True,
"KH": False,
},
entity_registry_enabled_default=False,
)
ENTITY_DESCRIPTION_MOISTURE = SIABinarySensorEntityDescription(
key=KEY_MOISTURE,
device_class=BinarySensorDeviceClass.MOISTURE,
code_consequences={
"WA": True,
"WH": False,
},
entity_registry_enabled_default=False,
)
def generate_binary_sensors(entry) -> Iterable[SIABinarySensor]:
"""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 account_data in entry.data[CONF_ACCOUNTS]:
yield SIABinarySensor(
port=entry.data[CONF_PORT],
account=account_data[CONF_ACCOUNT],
zone=SIA_HUB_ZONE,
ping_interval=account_data[CONF_PING_INTERVAL],
entity_description=ENTITY_DESCRIPTION_POWER,
unique_id=SIA_UNIQUE_ID_FORMAT_BINARY.format(
entry.entry_id,
account_data[CONF_ACCOUNT],
SIA_HUB_ZONE,
ENTITY_DESCRIPTION_POWER.device_class,
),
name=SIA_NAME_FORMAT_HUB.format(
entry.data[CONF_PORT],
account_data[CONF_ACCOUNT],
ENTITY_DESCRIPTION_POWER.device_class,
),
)
zones = entry.options[CONF_ACCOUNTS][account_data[CONF_ACCOUNT]][CONF_ZONES]
for zone in range(1, zones + 1):
yield SIABinarySensorSmoke(entry, account, zone)
yield SIABinarySensorMoisture(entry, account, zone)
yield SIABinarySensor(
port=entry.data[CONF_PORT],
account=account_data[CONF_ACCOUNT],
zone=zone,
ping_interval=account_data[CONF_PING_INTERVAL],
entity_description=ENTITY_DESCRIPTION_SMOKE,
unique_id=SIA_UNIQUE_ID_FORMAT_BINARY.format(
entry.entry_id,
account_data[CONF_ACCOUNT],
zone,
ENTITY_DESCRIPTION_SMOKE.device_class,
),
name=SIA_NAME_FORMAT.format(
entry.data[CONF_PORT],
account_data[CONF_ACCOUNT],
zone,
ENTITY_DESCRIPTION_SMOKE.device_class,
),
)
yield SIABinarySensor(
port=entry.data[CONF_PORT],
account=account_data[CONF_ACCOUNT],
zone=zone,
ping_interval=account_data[CONF_PING_INTERVAL],
entity_description=ENTITY_DESCRIPTION_MOISTURE,
unique_id=SIA_UNIQUE_ID_FORMAT_BINARY.format(
entry.entry_id,
account_data[CONF_ACCOUNT],
zone,
ENTITY_DESCRIPTION_MOISTURE.device_class,
),
name=SIA_NAME_FORMAT.format(
entry.data[CONF_PORT],
account_data[CONF_ACCOUNT],
zone,
ENTITY_DESCRIPTION_MOISTURE.device_class,
),
)
async def async_setup_entry(
@ -71,22 +155,10 @@ async def async_setup_entry(
async_add_entities(generate_binary_sensors(entry))
class SIABinarySensorBase(SIABaseEntity, BinarySensorEntity):
class SIABinarySensor(SIABaseEntity, BinarySensorEntity):
"""Class for SIA Binary Sensors."""
def __init__(
self,
entry: ConfigEntry,
account_data: dict[str, Any],
zone: int,
device_class: BinarySensorDeviceClass,
) -> None:
"""Initialize a base binary sensor."""
super().__init__(entry, account_data, zone)
self._attr_device_class = device_class
self._attr_unique_id = SIA_UNIQUE_ID_FORMAT_BINARY.format(
self._entry.entry_id, self._account, self._zone, self._attr_device_class
)
entity_description: SIABinarySensorEntityDescription
def handle_last_state(self, last_state: State | None) -> None:
"""Handle the last state."""
@ -98,66 +170,11 @@ class SIABinarySensorBase(SIABaseEntity, BinarySensorEntity):
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, BinarySensorDeviceClass.MOISTURE)
self._attr_entity_registry_enabled_default = False
def update_state(self, sia_event: SIAEvent) -> None:
def update_state(self, sia_event: SIAEvent) -> bool:
"""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, BinarySensorDeviceClass.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, BinarySensorDeviceClass.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
new_state = self.entity_description.code_consequences.get(sia_event.code)
if new_state is None:
return False
_LOGGER.debug("New state will be %s", new_state)
self._attr_is_on = bool(new_state)
return True

View file

@ -35,7 +35,6 @@ from .hub import SIAHub
_LOGGER = logging.getLogger(__name__)
HUB_SCHEMA = vol.Schema(
{
vol.Required(CONF_PORT): int,

View file

@ -1,28 +1,40 @@
"""Constants for the sia integration."""
from __future__ import annotations
from typing import Final
from homeassistant.const import Platform
PLATFORMS = [Platform.ALARM_CONTROL_PANEL, Platform.BINARY_SENSOR]
PLATFORMS: Final = [Platform.ALARM_CONTROL_PANEL, Platform.BINARY_SENSOR]
DOMAIN = "sia"
DOMAIN: Final = "sia"
ATTR_CODE = "last_code"
ATTR_ZONE = "last_zone"
ATTR_MESSAGE = "last_message"
ATTR_ID = "last_id"
ATTR_TIMESTAMP = "last_timestamp"
ATTR_CODE: Final = "last_code"
ATTR_ZONE: Final = "last_zone"
ATTR_MESSAGE: Final = "last_message"
ATTR_ID: Final = "last_id"
ATTR_TIMESTAMP: Final = "last_timestamp"
TITLE = "SIA Alarm on port {}"
CONF_ACCOUNT = "account"
CONF_ACCOUNTS = "accounts"
CONF_ADDITIONAL_ACCOUNTS = "additional_account"
CONF_ENCRYPTION_KEY = "encryption_key"
CONF_IGNORE_TIMESTAMPS = "ignore_timestamps"
CONF_PING_INTERVAL = "ping_interval"
CONF_ZONES = "zones"
TITLE: Final = "SIA Alarm on port {}"
CONF_ACCOUNT: Final = "account"
CONF_ACCOUNTS: Final = "accounts"
CONF_ADDITIONAL_ACCOUNTS: Final = "additional_account"
CONF_ENCRYPTION_KEY: Final = "encryption_key"
CONF_IGNORE_TIMESTAMPS: Final = "ignore_timestamps"
CONF_PING_INTERVAL: Final = "ping_interval"
CONF_ZONES: Final = "zones"
SIA_NAME_FORMAT = "{} - {} - zone {} - {}"
SIA_UNIQUE_ID_FORMAT_ALARM = "{}_{}_{}"
SIA_UNIQUE_ID_FORMAT_BINARY = "{}_{}_{}_{}"
SIA_HUB_ZONE = 0
SIA_NAME_FORMAT: Final = "{} - {} - zone {} - {}"
SIA_NAME_FORMAT_HUB: Final = "{} - {} - {}"
SIA_UNIQUE_ID_FORMAT_ALARM: Final = "{}_{}_{}"
SIA_UNIQUE_ID_FORMAT_BINARY: Final = "{}_{}_{}_{}"
SIA_UNIQUE_ID_FORMAT_HUB: Final = "{}_{}_{}"
SIA_HUB_ZONE: Final = 0
SIA_EVENT: Final = "sia_event_{}_{}"
SIA_EVENT = "sia_event_{}_{}"
KEY_ALARM: Final = "alarm_control_panel"
KEY_SMOKE: Final = "smoke"
KEY_MOISTURE: Final = "moisture"
KEY_POWER: Final = "power"
PREVIOUS_STATE: Final = "previous_state"

View file

@ -27,7 +27,6 @@ from .utils import get_event_data_from_sia_event
_LOGGER = logging.getLogger(__name__)
DEFAULT_TIMEBAND = (80, 40)
IGNORED_TIMEBAND = (3600, 1800)

View file

@ -2,52 +2,68 @@
from __future__ import annotations
from abc import abstractmethod
from dataclasses import dataclass
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.entity import DeviceInfo, EntityDescription
from homeassistant.helpers.event import async_call_later
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.helpers.typing import StateType
from .const import CONF_ACCOUNT, CONF_PING_INTERVAL, DOMAIN, SIA_EVENT, SIA_NAME_FORMAT
from .const import DOMAIN, SIA_EVENT, SIA_HUB_ZONE
from .utils import get_attr_from_sia_event, get_unavailability_interval
_LOGGER = logging.getLogger(__name__)
@dataclass
class SIARequiredKeysMixin:
"""Required keys for SIA entities."""
code_consequences: dict[str, StateType | bool]
@dataclass
class SIAEntityDescription(EntityDescription, SIARequiredKeysMixin):
"""Entity Description for SIA entities."""
class SIABaseEntity(RestoreEntity):
"""Base class for SIA entities."""
entity_description: SIAEntityDescription
def __init__(
self,
entry: ConfigEntry,
account_data: dict[str, Any],
zone: int,
device_class: str | None = None,
port: int,
account: str,
zone: int | None,
ping_interval: int,
entity_description: SIAEntityDescription,
unique_id: str,
name: 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 = 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.port = port
self.account = account
self.zone = zone
self.ping_interval = ping_interval
self.entity_description = entity_description
self._attr_unique_id = unique_id
self._attr_name = name
self._attr_device_info = DeviceInfo(
name=name,
identifiers={(DOMAIN, unique_id)},
via_device=(DOMAIN, f"{port}_{account}"),
)
self._cancel_availability_cb: CALLBACK_TYPE | None = None
self._attr_extra_state_attributes = {}
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.
@ -61,7 +77,7 @@ class SIABaseEntity(RestoreEntity):
self.async_on_remove(
async_dispatcher_connect(
self.hass,
SIA_EVENT.format(self._port, self._account),
SIA_EVENT.format(self.port, self.account),
self.async_handle_event,
)
)
@ -83,19 +99,18 @@ class SIABaseEntity(RestoreEntity):
@callback
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.
"""
"""Listen to dispatcher events for this port and account and update state and attributes."""
_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()
if int(sia_event.ri) not in (self.zone, SIA_HUB_ZONE):
return
self._attr_extra_state_attributes.update(get_attr_from_sia_event(sia_event))
state_changed = self.update_state(sia_event)
if state_changed:
self.async_reset_availability_cb()
self.async_write_ha_state()
@abstractmethod
def update_state(self, sia_event: SIAEvent) -> None:
def update_state(self, sia_event: SIAEvent) -> bool:
"""Do the entity specific state updates."""
@callback
@ -110,7 +125,7 @@ class SIABaseEntity(RestoreEntity):
"""Create a availability cb and return the callback."""
self._cancel_availability_cb = async_call_later(
self.hass,
get_unavailability_interval(self._ping_interval),
get_unavailability_interval(self.ping_interval),
self.async_set_unavailable,
)
@ -119,14 +134,3 @@ class SIABaseEntity(RestoreEntity):
"""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 DeviceInfo(
name=self._attr_name,
identifiers={(DOMAIN, self.unique_id)},
via_device=(DOMAIN, f"{self._port}_{self._account}"),
)