diff --git a/homeassistant/components/simplisafe/__init__.py b/homeassistant/components/simplisafe/__init__.py index 0c0595896ed..2514c5e9ed9 100644 --- a/homeassistant/components/simplisafe/__init__.py +++ b/homeassistant/components/simplisafe/__init__.py @@ -22,7 +22,11 @@ from homeassistant.helpers import ( config_validation as cv, device_registry as dr, ) -from homeassistant.helpers.dispatcher import async_dispatcher_send +from homeassistant.helpers.dispatcher import ( + async_dispatcher_connect, + async_dispatcher_send, +) +from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.service import verify_domain_control @@ -240,3 +244,73 @@ class SimpliSafe: tasks = [self._update_system(system) for system in self.systems.values()] await asyncio.gather(*tasks) + + +class SimpliSafeEntity(Entity): + """Define a base SimpliSafe entity.""" + + def __init__(self, system, name, *, serial=None): + """Initialize.""" + self._async_unsub_dispatcher_connect = None + self._attrs = {ATTR_SYSTEM_ID: system.system_id} + self._name = name + self._online = True + self._system = system + + if serial: + self._serial = serial + else: + self._serial = system.serial + + @property + def available(self): + """Return whether the entity is available.""" + # We can easily detect if the V3 system is offline, but no simple check exists + # for the V2 system. Therefore, we mark the entity as available if: + # 1. We can verify that the system is online (assuming True if we can't) + # 2. We can verify that the entity is online + system_offline = self._system.version == 3 and self._system.offline + return not system_offline and self._online + + @property + def device_info(self): + """Return device registry information for this entity.""" + return { + "identifiers": {(DOMAIN, self._system.system_id)}, + "manufacturer": "SimpliSafe", + "model": self._system.version, + "name": self._name, + "via_device": (DOMAIN, self._system.serial), + } + + @property + def device_state_attributes(self): + """Return the state attributes.""" + return self._attrs + + @property + def name(self): + """Return the name of the entity.""" + return f"{self._system.address} {self._name}" + + @property + def unique_id(self): + """Return the unique ID of the entity.""" + return self._serial + + async def async_added_to_hass(self): + """Register callbacks.""" + + @callback + def update(): + """Update the state.""" + self.async_schedule_update_ha_state(True) + + self._async_unsub_dispatcher_connect = async_dispatcher_connect( + self.hass, TOPIC_UPDATE, update + ) + + async def async_will_remove_from_hass(self) -> None: + """Disconnect dispatcher listener when removed.""" + if self._async_unsub_dispatcher_connect: + self._async_unsub_dispatcher_connect() diff --git a/homeassistant/components/simplisafe/alarm_control_panel.py b/homeassistant/components/simplisafe/alarm_control_panel.py index d44a1c7760a..bbd9a5d4e51 100644 --- a/homeassistant/components/simplisafe/alarm_control_panel.py +++ b/homeassistant/components/simplisafe/alarm_control_panel.py @@ -2,7 +2,7 @@ import logging import re -from simplipy.sensor import SensorTypes +from simplipy.entity import EntityTypes from simplipy.system import SystemStates from homeassistant.components.alarm_control_panel import ( @@ -16,11 +16,10 @@ from homeassistant.const import ( STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, ) -from homeassistant.core import callback -from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.util.dt import utc_from_timestamp -from .const import DATA_CLIENT, DOMAIN, TOPIC_UPDATE +from . import SimpliSafeEntity +from .const import DATA_CLIENT, DOMAIN _LOGGER = logging.getLogger(__name__) @@ -33,7 +32,6 @@ ATTR_LAST_EVENT_SENSOR_TYPE = "last_event_sensor_type" ATTR_LAST_EVENT_TIMESTAMP = "last_event_timestamp" ATTR_LAST_EVENT_TYPE = "last_event_type" ATTR_RF_JAMMING = "rf_jamming" -ATTR_SYSTEM_ID = "system_id" ATTR_WALL_POWER_LEVEL = "wall_power_level" ATTR_WIFI_STRENGTH = "wifi_strength" @@ -55,18 +53,16 @@ async def async_setup_entry(hass, entry, async_add_entities): ) -class SimpliSafeAlarm(AlarmControlPanel): +class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanel): """Representation of a SimpliSafe alarm.""" def __init__(self, simplisafe, system, code): """Initialize the SimpliSafe alarm.""" - self._async_unsub_dispatcher_connect = None - self._attrs = {ATTR_SYSTEM_ID: system.system_id} + super().__init__(system, "Alarm Control Panel") self._changed_by = None self._code = code self._simplisafe = simplisafe self._state = None - self._system = system # Some properties only exist for V2 or V3 systems: for prop in ( @@ -93,39 +89,11 @@ class SimpliSafeAlarm(AlarmControlPanel): return FORMAT_NUMBER return FORMAT_TEXT - @property - def device_info(self): - """Return device registry information for this entity.""" - return { - "identifiers": {(DOMAIN, self._system.system_id)}, - "manufacturer": "SimpliSafe", - "model": self._system.version, - # The name should become more dynamic once we deduce a way to - # get various other sensors from SimpliSafe in a reliable manner: - "name": "Keypad", - "via_device": (DOMAIN, self._system.serial), - } - - @property - def device_state_attributes(self): - """Return the state attributes.""" - return self._attrs - - @property - def name(self): - """Return the name of the entity.""" - return self._system.address - @property def state(self): """Return the state of the entity.""" return self._state - @property - def unique_id(self): - """Return the unique ID of the entity.""" - return self._system.system_id - def _validate_code(self, code, state): """Validate given code.""" check = self._code is None or code == self._code @@ -133,18 +101,6 @@ class SimpliSafeAlarm(AlarmControlPanel): _LOGGER.warning("Wrong code entered for %s", state) return check - async def async_added_to_hass(self): - """Register callbacks.""" - - @callback - def update(): - """Update the state.""" - self.async_schedule_update_ha_state(True) - - self._async_unsub_dispatcher_connect = async_dispatcher_connect( - self.hass, TOPIC_UPDATE, update - ) - async def async_alarm_disarm(self, code=None): """Send disarm command.""" if not self._validate_code(code, "disarming"): @@ -174,6 +130,7 @@ class SimpliSafeAlarm(AlarmControlPanel): self._changed_by = event_data["pinName"] if self._system.state == SystemStates.error: + self._online = False return if self._system.state == SystemStates.off: @@ -195,15 +152,10 @@ class SimpliSafeAlarm(AlarmControlPanel): ATTR_ALARM_ACTIVE: self._system.alarm_going_off, ATTR_LAST_EVENT_INFO: last_event["info"], ATTR_LAST_EVENT_SENSOR_NAME: last_event["sensorName"], - ATTR_LAST_EVENT_SENSOR_TYPE: SensorTypes(last_event["sensorType"]).name, + ATTR_LAST_EVENT_SENSOR_TYPE: EntityTypes(last_event["sensorType"]).name, ATTR_LAST_EVENT_TIMESTAMP: utc_from_timestamp( last_event["eventTimestamp"] ), ATTR_LAST_EVENT_TYPE: last_event["eventType"], } ) - - async def async_will_remove_from_hass(self) -> None: - """Disconnect dispatcher listener when removed.""" - if self._async_unsub_dispatcher_connect: - self._async_unsub_dispatcher_connect() diff --git a/homeassistant/components/simplisafe/manifest.json b/homeassistant/components/simplisafe/manifest.json index 96b337def55..254e947ed25 100644 --- a/homeassistant/components/simplisafe/manifest.json +++ b/homeassistant/components/simplisafe/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/simplisafe", "requirements": [ - "simplisafe-python==5.0.1" + "simplisafe-python==5.1.0" ], "dependencies": [], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index 6f0b40230ed..ccf732ce2ae 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1769,7 +1769,7 @@ shodan==1.19.0 simplepush==1.1.4 # homeassistant.components.simplisafe -simplisafe-python==5.0.1 +simplisafe-python==5.1.0 # homeassistant.components.sisyphus sisyphus-control==2.2.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 691e8d413d5..99b9c744041 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -546,7 +546,7 @@ rxv==0.6.0 samsungctl[websocket]==0.7.1 # homeassistant.components.simplisafe -simplisafe-python==5.0.1 +simplisafe-python==5.1.0 # homeassistant.components.sleepiq sleepyq==0.7