From 2ff356393ca9bdf2969b7bd802517c38aab7dadb Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Thu, 21 Oct 2021 04:54:50 -0600 Subject: [PATCH] Clean up SimpliSafe entity inheritance structure (#58063) * Migrate SimpliSafe to new web-based authentication * Ensure we're storing data correctly * Re-organize SimpliSafe device structure * Constants * More work * Code review --- .../components/simplisafe/__init__.py | 60 +++++++------------ .../simplisafe/alarm_control_panel.py | 2 +- .../components/simplisafe/binary_sensor.py | 28 ++++----- homeassistant/components/simplisafe/lock.py | 20 +++---- homeassistant/components/simplisafe/sensor.py | 19 ++++-- 5 files changed, 57 insertions(+), 72 deletions(-) diff --git a/homeassistant/components/simplisafe/__init__.py b/homeassistant/components/simplisafe/__init__.py index 28ece7d45cd..d41d7b03eda 100644 --- a/homeassistant/components/simplisafe/__init__.py +++ b/homeassistant/components/simplisafe/__init__.py @@ -7,8 +7,7 @@ from datetime import timedelta from typing import TYPE_CHECKING, cast from simplipy import API -from simplipy.device.sensor.v2 import SensorV2 -from simplipy.device.sensor.v3 import SensorV3 +from simplipy.device import Device from simplipy.errors import ( EndpointUnavailableError, InvalidCredentialsError, @@ -62,6 +61,8 @@ from .const import ( EVENT_SIMPLISAFE_NOTIFICATION = "SIMPLISAFE_NOTIFICATION" +DEFAULT_ENTITY_MODEL = "alarm_control_panel" +DEFAULT_ENTITY_NAME = "Alarm Control Panel" DEFAULT_SCAN_INTERVAL = timedelta(seconds=30) DEFAULT_SOCKET_MIN_RETRY = 15 @@ -159,7 +160,7 @@ async def async_register_base_station( device_registry = await dr.async_get_registry(hass) device_registry.async_get_or_create( config_entry_id=entry.entry_id, - identifiers={(DOMAIN, system.serial)}, + identifiers={(DOMAIN, system.system_id)}, manufacturer="SimpliSafe", model=system.version, name=system.address, @@ -424,29 +425,34 @@ class SimpliSafeEntity(CoordinatorEntity): self, simplisafe: SimpliSafe, system: SystemV2 | SystemV3, - name: str, *, - serial: str | None = None, + device: Device | None = None, ) -> None: """Initialize.""" assert simplisafe.coordinator super().__init__(simplisafe.coordinator) - if serial: - self._serial = serial + if device: + model = device.type.name + device_name = device.name + serial = device.serial else: - self._serial = system.serial + model = DEFAULT_ENTITY_MODEL + device_name = DEFAULT_ENTITY_NAME + serial = system.serial self._attr_extra_state_attributes = {ATTR_SYSTEM_ID: system.system_id} self._attr_device_info = { - "identifiers": {(DOMAIN, system.system_id)}, + "identifiers": {(DOMAIN, serial)}, "manufacturer": "SimpliSafe", - "model": str(system.version), - "name": name, - "via_device": (DOMAIN, system.serial), + "model": model, + "name": device_name, + "via_device": (DOMAIN, system.system_id), } - self._attr_name = f"{system.address} {name}" - self._attr_unique_id = self._serial + + self._attr_name = f"{system.address} {device_name} {' '.join([w.title() for w in model.split('_')])}" + self._attr_unique_id = serial + self._device = device self._online = True self._simplisafe = simplisafe self._system = system @@ -481,29 +487,3 @@ class SimpliSafeEntity(CoordinatorEntity): def async_update_from_rest_api(self) -> None: """Update the entity with the provided REST API data.""" raise NotImplementedError() - - -class SimpliSafeBaseSensor(SimpliSafeEntity): - """Define a SimpliSafe base (binary) sensor.""" - - def __init__( - self, - simplisafe: SimpliSafe, - system: SystemV2 | SystemV3, - sensor: SensorV2 | SensorV3, - ) -> None: - """Initialize.""" - super().__init__(simplisafe, system, sensor.name, serial=sensor.serial) - - self._attr_device_info = { - "identifiers": {(DOMAIN, sensor.serial)}, - "manufacturer": "SimpliSafe", - "model": sensor.type.name, - "name": sensor.name, - "via_device": (DOMAIN, system.serial), - } - - human_friendly_name = " ".join([w.title() for w in sensor.type.name.split("_")]) - self._attr_name = f"{super().name} {human_friendly_name}" - - self._sensor = sensor diff --git a/homeassistant/components/simplisafe/alarm_control_panel.py b/homeassistant/components/simplisafe/alarm_control_panel.py index 278d7579edf..1355669129d 100644 --- a/homeassistant/components/simplisafe/alarm_control_panel.py +++ b/homeassistant/components/simplisafe/alarm_control_panel.py @@ -80,7 +80,7 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanelEntity): def __init__(self, simplisafe: SimpliSafe, system: SystemV2 | SystemV3) -> None: """Initialize the SimpliSafe alarm.""" - super().__init__(simplisafe, system, "Alarm Control Panel") + super().__init__(simplisafe, system) if code := self._simplisafe.entry.options.get(CONF_CODE): if code.isdigit(): diff --git a/homeassistant/components/simplisafe/binary_sensor.py b/homeassistant/components/simplisafe/binary_sensor.py index e7b156a5b1c..f276a5fea66 100644 --- a/homeassistant/components/simplisafe/binary_sensor.py +++ b/homeassistant/components/simplisafe/binary_sensor.py @@ -2,9 +2,7 @@ from __future__ import annotations from simplipy.device import DeviceTypes -from simplipy.device.sensor.v2 import SensorV2 from simplipy.device.sensor.v3 import SensorV3 -from simplipy.system.v2 import SystemV2 from simplipy.system.v3 import SystemV3 from homeassistant.components.binary_sensor import ( @@ -22,7 +20,7 @@ from homeassistant.const import ENTITY_CATEGORY_DIAGNOSTIC from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import SimpliSafe, SimpliSafeBaseSensor +from . import SimpliSafe, SimpliSafeEntity from .const import DATA_CLIENT, DOMAIN, LOGGER SUPPORTED_BATTERY_SENSOR_TYPES = [ @@ -77,45 +75,45 @@ async def async_setup_entry( async_add_entities(sensors) -class TriggeredBinarySensor(SimpliSafeBaseSensor, BinarySensorEntity): +class TriggeredBinarySensor(SimpliSafeEntity, BinarySensorEntity): """Define a binary sensor related to whether an entity has been triggered.""" def __init__( self, simplisafe: SimpliSafe, - system: SystemV2 | SystemV3, - sensor: SensorV2 | SensorV3, + system: SystemV3, + sensor: SensorV3, device_class: str, ) -> None: """Initialize.""" - super().__init__(simplisafe, system, sensor) + super().__init__(simplisafe, system, device=sensor) self._attr_device_class = device_class + self._device: SensorV3 @callback def async_update_from_rest_api(self) -> None: """Update the entity with the provided REST API data.""" - self._attr_is_on = self._sensor.triggered + self._attr_is_on = self._device.triggered -class BatteryBinarySensor(SimpliSafeBaseSensor, BinarySensorEntity): +class BatteryBinarySensor(SimpliSafeEntity, BinarySensorEntity): """Define a SimpliSafe battery binary sensor entity.""" _attr_device_class = DEVICE_CLASS_BATTERY _attr_entity_category = ENTITY_CATEGORY_DIAGNOSTIC def __init__( - self, - simplisafe: SimpliSafe, - system: SystemV2 | SystemV3, - sensor: SensorV2 | SensorV3, + self, simplisafe: SimpliSafe, system: SystemV3, sensor: SensorV3 ) -> None: """Initialize.""" - super().__init__(simplisafe, system, sensor) + super().__init__(simplisafe, system, device=sensor) + self._attr_name = f"{super().name} Battery" self._attr_unique_id = f"{super().unique_id}-battery" + self._device: SensorV3 @callback def async_update_from_rest_api(self) -> None: """Update the entity with the provided REST API data.""" - self._attr_is_on = self._sensor.low_battery + self._attr_is_on = self._device.low_battery diff --git a/homeassistant/components/simplisafe/lock.py b/homeassistant/components/simplisafe/lock.py index 34fa141745b..3375a413edb 100644 --- a/homeassistant/components/simplisafe/lock.py +++ b/homeassistant/components/simplisafe/lock.py @@ -42,16 +42,16 @@ class SimpliSafeLock(SimpliSafeEntity, LockEntity): def __init__(self, simplisafe: SimpliSafe, system: SystemV3, lock: Lock) -> None: """Initialize.""" - super().__init__(simplisafe, system, lock.name, serial=lock.serial) + super().__init__(simplisafe, system, device=lock) - self._lock = lock + self._device: Lock async def async_lock(self, **kwargs: Any) -> None: """Lock the lock.""" try: - await self._lock.async_lock() + await self._device.async_lock() except SimplipyError as err: - LOGGER.error('Error while locking "%s": %s', self._lock.name, err) + LOGGER.error('Error while locking "%s": %s', self._device.name, err) return self._attr_is_locked = True @@ -60,9 +60,9 @@ class SimpliSafeLock(SimpliSafeEntity, LockEntity): async def async_unlock(self, **kwargs: Any) -> None: """Unlock the lock.""" try: - await self._lock.async_unlock() + await self._device.async_unlock() except SimplipyError as err: - LOGGER.error('Error while unlocking "%s": %s', self._lock.name, err) + LOGGER.error('Error while unlocking "%s": %s', self._device.name, err) return self._attr_is_locked = False @@ -73,10 +73,10 @@ class SimpliSafeLock(SimpliSafeEntity, LockEntity): """Update the entity with the provided REST API data.""" self._attr_extra_state_attributes.update( { - ATTR_LOCK_LOW_BATTERY: self._lock.lock_low_battery, - ATTR_PIN_PAD_LOW_BATTERY: self._lock.pin_pad_low_battery, + ATTR_LOCK_LOW_BATTERY: self._device.lock_low_battery, + ATTR_PIN_PAD_LOW_BATTERY: self._device.pin_pad_low_battery, } ) - self._attr_is_jammed = self._lock.state == LockStates.jammed - self._attr_is_locked = self._lock.state == LockStates.locked + self._attr_is_jammed = self._device.state == LockStates.jammed + self._attr_is_locked = self._device.state == LockStates.locked diff --git a/homeassistant/components/simplisafe/sensor.py b/homeassistant/components/simplisafe/sensor.py index 96aed33979d..97edd3008dd 100644 --- a/homeassistant/components/simplisafe/sensor.py +++ b/homeassistant/components/simplisafe/sensor.py @@ -1,8 +1,9 @@ """Support for SimpliSafe freeze sensor.""" -from typing import TYPE_CHECKING +from __future__ import annotations from simplipy.device import DeviceTypes from simplipy.device.sensor.v3 import SensorV3 +from simplipy.system.v3 import SystemV3 from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT, SensorEntity from homeassistant.config_entries import ConfigEntry @@ -10,7 +11,7 @@ from homeassistant.const import DEVICE_CLASS_TEMPERATURE, TEMP_FAHRENHEIT from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import SimpliSafeBaseSensor +from . import SimpliSafe, SimpliSafeEntity from .const import DATA_CLIENT, DOMAIN, LOGGER @@ -33,16 +34,22 @@ async def async_setup_entry( async_add_entities(sensors) -class SimplisafeFreezeSensor(SimpliSafeBaseSensor, SensorEntity): +class SimplisafeFreezeSensor(SimpliSafeEntity, SensorEntity): """Define a SimpliSafe freeze sensor entity.""" _attr_device_class = DEVICE_CLASS_TEMPERATURE _attr_native_unit_of_measurement = TEMP_FAHRENHEIT _attr_state_class = STATE_CLASS_MEASUREMENT + def __init__( + self, simplisafe: SimpliSafe, system: SystemV3, sensor: SensorV3 + ) -> None: + """Initialize.""" + super().__init__(simplisafe, system, device=sensor) + + self._device: SensorV3 + @callback def async_update_from_rest_api(self) -> None: """Update the entity with the provided REST API data.""" - if TYPE_CHECKING: - assert isinstance(self._sensor, SensorV3) - self._attr_native_value = self._sensor.temperature + self._attr_native_value = self._device.temperature