diff --git a/homeassistant/components/simplisafe/__init__.py b/homeassistant/components/simplisafe/__init__.py index cda90cd8d1b..bce2bc2f2a2 100644 --- a/homeassistant/components/simplisafe/__init__.py +++ b/homeassistant/components/simplisafe/__init__.py @@ -130,6 +130,7 @@ async def async_setup_entry(hass, config_entry): systems = await api.get_systems() simplisafe = SimpliSafe(hass, config_entry, systems) + await simplisafe.async_update() hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = simplisafe hass.async_create_task( @@ -139,6 +140,8 @@ async def async_setup_entry(hass, config_entry): async def refresh(event_time): """Refresh data from the SimpliSafe account.""" await simplisafe.async_update() + _LOGGER.debug('Updated data for all SimpliSafe systems') + async_dispatcher_send(hass, TOPIC_UPDATE) hass.data[DOMAIN][DATA_LISTENER][ config_entry.entry_id] = async_track_time_interval( @@ -193,25 +196,34 @@ class SimpliSafe: """Initialize.""" self._config_entry = config_entry self._hass = hass + self.last_event_data = {} self.systems = systems + async def _update_system(self, system): + """Update a system.""" + try: + await system.update() + latest_event = await system.get_latest_event() + except SimplipyError as err: + _LOGGER.error( + 'SimpliSafe error while updating "%s": %s', + system.address, err) + return + except Exception as err: # pylint: disable=broad-except + _LOGGER.error( + 'Unknown error while updating "%s": %s', system.address, err) + return + + self.last_event_data[system.system_id] = latest_event + + if system.api.refresh_token_dirty: + _async_save_refresh_token( + self._hass, self._config_entry, system.api.refresh_token) + async def async_update(self): """Get updated data from SimpliSafe.""" - systems = self.systems.values() - tasks = [system.update() for system in systems] + tasks = [ + self._update_system(system) for system in self.systems.values() + ] - results = await asyncio.gather(*tasks, return_exceptions=True) - for system, result in zip(systems, results): - if isinstance(result, Exception): - _LOGGER.error( - 'There was error updating "%s": %s', system.address, - result) - continue - - if system.api.refresh_token_dirty: - _async_save_refresh_token( - self._hass, self._config_entry, system.api.refresh_token) - - _LOGGER.debug('Updated status of "%s"', system.address) - async_dispatcher_send( - self._hass, TOPIC_UPDATE.format(system.system_id)) + await asyncio.gather(*tasks) diff --git a/homeassistant/components/simplisafe/alarm_control_panel.py b/homeassistant/components/simplisafe/alarm_control_panel.py index e0a7799b7d6..79d4774ffb3 100644 --- a/homeassistant/components/simplisafe/alarm_control_panel.py +++ b/homeassistant/components/simplisafe/alarm_control_panel.py @@ -1,13 +1,19 @@ + """Support for SimpliSafe alarm control panels.""" import logging import re -import homeassistant.components.alarm_control_panel as alarm +from simplipy.sensor import SensorTypes +from simplipy.system import SystemStates + +from homeassistant.components.alarm_control_panel import ( + FORMAT_NUMBER, FORMAT_TEXT, AlarmControlPanel) from homeassistant.const import ( CONF_CODE, STATE_ALARM_ARMED_AWAY, 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 @@ -16,6 +22,11 @@ _LOGGER = logging.getLogger(__name__) ATTR_ALARM_ACTIVE = 'alarm_active' ATTR_BATTERY_BACKUP_POWER_LEVEL = 'battery_backup_power_level' ATTR_GSM_STRENGTH = 'gsm_strength' +ATTR_LAST_EVENT_INFO = 'last_event_info' +ATTR_LAST_EVENT_SENSOR_NAME = 'last_event_sensor_name' +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' @@ -32,21 +43,23 @@ async def async_setup_entry(hass, entry, async_add_entities): """Set up a SimpliSafe alarm control panel based on a config entry.""" simplisafe = hass.data[DOMAIN][DATA_CLIENT][entry.entry_id] async_add_entities([ - SimpliSafeAlarm(system, entry.data.get(CONF_CODE)) + SimpliSafeAlarm(simplisafe, system, entry.data.get(CONF_CODE)) for system in simplisafe.systems.values() ], True) -class SimpliSafeAlarm(alarm.AlarmControlPanel): +class SimpliSafeAlarm(AlarmControlPanel): """Representation of a SimpliSafe alarm.""" - def __init__(self, system, code): + def __init__(self, simplisafe, system, code): """Initialize the SimpliSafe alarm.""" self._async_unsub_dispatcher_connect = None self._attrs = {ATTR_SYSTEM_ID: system.system_id} + self._changed_by = None self._code = code - self._system = system + self._simplisafe = simplisafe self._state = None + self._system = system # Some properties only exist for V2 or V3 systems: for prop in ( @@ -55,14 +68,19 @@ class SimpliSafeAlarm(alarm.AlarmControlPanel): if hasattr(system, prop): self._attrs[prop] = getattr(system, prop) + @property + def changed_by(self): + """Return info about who changed the alarm last.""" + return self._changed_by + @property def code_format(self): """Return one or more digits/characters.""" if not self._code: return None if isinstance(self._code, str) and re.search('^\\d+$', self._code): - return alarm.FORMAT_NUMBER - return alarm.FORMAT_TEXT + return FORMAT_NUMBER + return FORMAT_TEXT @property def device_info(self): @@ -86,17 +104,17 @@ class SimpliSafeAlarm(alarm.AlarmControlPanel): @property def name(self): - """Return the name of the device.""" + """Return the name of the entity.""" return self._system.address @property def state(self): - """Return the state of the device.""" + """Return the state of the entity.""" return self._state @property def unique_id(self): - """Return the unique ID.""" + """Return the unique ID of the entity.""" return self._system.system_id def _validate_code(self, code, state): @@ -116,11 +134,6 @@ class SimpliSafeAlarm(alarm.AlarmControlPanel): 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() - async def async_alarm_disarm(self, code=None): """Send disarm command.""" if not self._validate_code(code, 'disarming'): @@ -144,9 +157,10 @@ class SimpliSafeAlarm(alarm.AlarmControlPanel): async def async_update(self): """Update alarm status.""" - from simplipy.system import SystemStates + event_data = self._simplisafe.last_event_data[self._system.system_id] - self._attrs[ATTR_ALARM_ACTIVE] = self._system.alarm_going_off + if event_data['pinName']: + self._changed_by = event_data['pinName'] if self._system.state == SystemStates.error: return @@ -161,3 +175,20 @@ class SimpliSafeAlarm(alarm.AlarmControlPanel): self._state = STATE_ALARM_ARMED_AWAY else: self._state = None + + last_event = self._simplisafe.last_event_data[self._system.system_id] + self._attrs.update({ + 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_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/const.py b/homeassistant/components/simplisafe/const.py index 437197878e0..8e9e593df12 100644 --- a/homeassistant/components/simplisafe/const.py +++ b/homeassistant/components/simplisafe/const.py @@ -7,4 +7,4 @@ DATA_CLIENT = 'client' DEFAULT_SCAN_INTERVAL = timedelta(seconds=30) -TOPIC_UPDATE = 'update_{0}' +TOPIC_UPDATE = 'update' diff --git a/homeassistant/components/simplisafe/manifest.json b/homeassistant/components/simplisafe/manifest.json index 095de440ab6..130a9d23a3a 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/components/simplisafe", "requirements": [ - "simplisafe-python==4.0.1" + "simplisafe-python==4.2.0" ], "dependencies": [], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index 569af7b9609..81a1600f1d5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1691,7 +1691,7 @@ shodan==1.13.0 simplepush==1.1.4 # homeassistant.components.simplisafe -simplisafe-python==4.0.1 +simplisafe-python==4.2.0 # homeassistant.components.sisyphus sisyphus-control==2.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index cfc6e93e620..cc11ae1699c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -346,7 +346,7 @@ ring_doorbell==0.2.3 rxv==0.6.0 # homeassistant.components.simplisafe -simplisafe-python==4.0.1 +simplisafe-python==4.2.0 # homeassistant.components.sleepiq sleepyq==0.7