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
This commit is contained in:
Aaron Bach 2021-10-21 04:54:50 -06:00 committed by GitHub
parent c7ff6eb5ee
commit 2ff356393c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 57 additions and 72 deletions

View File

@ -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

View File

@ -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():

View File

@ -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

View File

@ -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

View File

@ -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