diff --git a/custom_components/adtpulse/alarm_control_panel.py b/custom_components/adtpulse/alarm_control_panel.py index 5f19a26..607fefd 100644 --- a/custom_components/adtpulse/alarm_control_panel.py +++ b/custom_components/adtpulse/alarm_control_panel.py @@ -25,7 +25,6 @@ AddEntitiesCallback, async_get_current_platform, ) -from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util import as_local from pyadtpulse.alarm_panel import ( ADT_ALARM_ARMING, @@ -37,7 +36,8 @@ ) from pyadtpulse.site import ADTPulseSite -from .const import ADTPULSE_DATA_ATTRIBUTION, ADTPULSE_DOMAIN +from .base_entity import ADTPulseEntity +from .const import ADTPULSE_DOMAIN from .coordinator import ADTPulseDataUpdateCoordinator from .utils import ( get_alarm_unique_id, @@ -96,17 +96,13 @@ async def async_setup_entry( ) -class ADTPulseAlarm( - CoordinatorEntity[ADTPulseDataUpdateCoordinator], alarm.AlarmControlPanelEntity -): +class ADTPulseAlarm(ADTPulseEntity, alarm.AlarmControlPanelEntity): """An alarm_control_panel implementation for ADT Pulse.""" def __init__(self, coordinator: ADTPulseDataUpdateCoordinator, site: ADTPulseSite): """Initialize the alarm control panel.""" LOG.debug("%s: adding alarm control panel for %s", ADTPULSE_DOMAIN, site.id) self._name = f"ADT Alarm Panel - Site {site.id}" - self._site = site - self._alarm = site.alarm_control_panel self._assumed_state: str | None = None super().__init__(coordinator, self._name) @@ -127,11 +123,6 @@ def state(self) -> str: def assumed_state(self) -> bool: return self._assumed_state is None - @property - def attribution(self) -> str | None: - """Return API data attribution.""" - return ADTPULSE_DATA_ATTRIBUTION - @property def icon(self) -> str: """Return the icon.""" @@ -232,15 +223,6 @@ async def async_alarm_arm_force_stay(self) -> None: self._site.async_arm_home(force_arm=True), STATE_ALARM_ARMED_HOME ) - @property - def name(self) -> str | None: - """Return the name of the alarm.""" - return None - - @property - def has_entity_name(self) -> bool: - return True - @property def extra_state_attributes(self) -> dict: """Return the state attributes.""" diff --git a/custom_components/adtpulse/base_entity.py b/custom_components/adtpulse/base_entity.py index 59d3e25..d741d4c 100644 --- a/custom_components/adtpulse/base_entity.py +++ b/custom_components/adtpulse/base_entity.py @@ -1,12 +1,17 @@ """ADT Pulse Entity Base class.""" from __future__ import annotations +from logging import getLogger +from typing import Any, Mapping + from homeassistant.core import callback from homeassistant.helpers.update_coordinator import CoordinatorEntity -from .const import LOG +from .const import ADTPULSE_DATA_ATTRIBUTION from .coordinator import ADTPulseDataUpdateCoordinator +LOG = getLogger(__name__) + class ADTPulseEntity(CoordinatorEntity[ADTPulseDataUpdateCoordinator]): """Base Entity class for ADT Pulse devices.""" @@ -19,14 +24,26 @@ def __init__(self, coordinator: ADTPulseDataUpdateCoordinator, name: str): name (str): entity name """ self._name = name - + # save references to commonly used objects + self._pulse_connection = coordinator.adtpulse + self._site = self._pulse_connection.site + self._gateway = self._site.gateway + self._alarm = self._site.alarm_control_panel self._attrs: dict = {} super().__init__(coordinator) + # Base level properties that can be overridden by subclasses + @property + def name(self) -> str | None: + """Return the display name for this sensor. + + Should generally be none since using has_entity_name.""" + return None + @property - def name(self) -> str: - """Return the display name for this sensor.""" - return self._name + def has_entity_name(self) -> bool: + """Returns has_entity_name. Should generally be true.""" + return True @property def icon(self) -> str: @@ -38,13 +55,25 @@ def icon(self) -> str: return "mdi:gauge" @property - def extra_state_attributes(self) -> dict: + def extra_state_attributes(self) -> Mapping[str, Any] | None: """Return the device state attributes.""" return self._attrs + @property + def is_available(self) -> bool: + """Returns whether an entity is available. + + Generally false if gateway is offline.""" + return self._gateway.is_online + + @property + def attribution(self) -> str: + """Return API data attribution.""" + return ADTPULSE_DATA_ATTRIBUTION + @callback def _handle_coordinator_update(self) -> None: """Call update method.""" - LOG.debug(f"Scheduling update ADT Pulse entity {self._name}") + LOG.debug("Scheduling update ADT Pulse entity %s", self._name) # inform HASS that ADT Pulse data for this entity has been updated self.async_write_ha_state() diff --git a/custom_components/adtpulse/binary_sensor.py b/custom_components/adtpulse/binary_sensor.py index a96f410..67adb9c 100644 --- a/custom_components/adtpulse/binary_sensor.py +++ b/custom_components/adtpulse/binary_sensor.py @@ -20,12 +20,12 @@ from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util import as_local from pyadtpulse.site import ADTPulseSite from pyadtpulse.zones import ADTPulseZoneData -from .const import ADTPULSE_DATA_ATTRIBUTION, ADTPULSE_DOMAIN +from .base_entity import ADTPulseEntity +from .const import ADTPULSE_DOMAIN from .coordinator import ADTPulseDataUpdateCoordinator from .utils import ( get_alarm_unique_id, @@ -103,9 +103,7 @@ async def async_setup_entry( async_add_entities(entities) -class ADTPulseZoneSensor( - CoordinatorEntity[ADTPulseDataUpdateCoordinator], BinarySensorEntity -): +class ADTPulseZoneSensor(ADTPulseEntity, BinarySensorEntity): """HASS zone binary sensor implementation for ADT Pulse.""" # zone = {'id': 'sensor-12', 'name': 'South Office Motion', @@ -167,7 +165,6 @@ def __init__( ) else: LOG.debug("%s: adding zone sensor for site %s", ADTPULSE_DOMAIN, site.id) - self._site = site self._zone_id = zone_id self._is_trouble_indicator = trouble_indicator self._my_zone = self._get_my_zone(site, zone_id) @@ -187,10 +184,6 @@ def name(self) -> str | None: return "Trouble" return None - @property - def has_entity_name(self) -> bool: - return True - @property def unique_id(self) -> str: """Return HA unique id.""" @@ -255,11 +248,6 @@ def device_info(self) -> DeviceInfo: manufacturer="ADT", ) - @property - def attribution(self) -> str: - """Return API data attribution.""" - return ADTPULSE_DATA_ATTRIBUTION - @callback def _handle_coordinator_update(self) -> None: LOG.debug( @@ -271,9 +259,7 @@ def _handle_coordinator_update(self) -> None: self.async_write_ha_state() -class ADTPulseGatewaySensor( - CoordinatorEntity[ADTPulseDataUpdateCoordinator], BinarySensorEntity -): +class ADTPulseGatewaySensor(ADTPulseEntity, BinarySensorEntity): """HASS Gateway Online Binary Sensor.""" def __init__(self, coordinator: ADTPulseDataUpdateCoordinator, site: ADTPulseSite): @@ -288,9 +274,8 @@ def __init__(self, coordinator: ADTPulseDataUpdateCoordinator, site: ADTPulseSit "%s: adding gateway status sensor for site %s", ADTPULSE_DOMAIN, site.name ) self._gateway = site.gateway - self._site = site self._device_class = BinarySensorDeviceClass.CONNECTIVITY - self._name = f"ADT Pulse Gateway Status - Site: {self._site.name}" + self._name = f"ADT Pulse Gateway Status - Site: {site.name}" super().__init__(coordinator, self._name) @property @@ -298,14 +283,6 @@ def is_on(self) -> bool: """Return if gateway is online.""" return self._gateway.is_online - @property - def name(self) -> str | None: - return None - - @property - def has_entity_name(self) -> bool: - return True - # FIXME: Gateways only support one site? @property def unique_id(self) -> str: @@ -318,11 +295,6 @@ def icon(self) -> str: return "mdi:lan-connect" return "mdi:lan-disconnect" - @property - def attribution(self) -> str | None: - """Return API data attribution.""" - return ADTPULSE_DATA_ATTRIBUTION - @property def extra_state_attributes(self) -> Mapping[str, Any]: """Return the device state attributes."""