Remove simplisafe websocket (#50213)

This commit is contained in:
Aaron Bach 2021-05-07 07:41:37 -06:00 committed by GitHub
parent 86393bdbba
commit d4601e00fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 4 additions and 284 deletions

View File

@ -3,18 +3,7 @@ import asyncio
from uuid import UUID from uuid import UUID
from simplipy import API from simplipy import API
from simplipy.entity import EntityTypes
from simplipy.errors import EndpointUnavailable, InvalidCredentialsError, SimplipyError from simplipy.errors import EndpointUnavailable, InvalidCredentialsError, SimplipyError
from simplipy.websocket import (
EVENT_CAMERA_MOTION_DETECTED,
EVENT_CONNECTION_LOST,
EVENT_CONNECTION_RESTORED,
EVENT_DOORBELL_DETECTED,
EVENT_ENTRY_DELAY,
EVENT_LOCK_LOCKED,
EVENT_LOCK_UNLOCKED,
EVENT_SECRET_ALERT_TRIGGERED,
)
import voluptuous as vol import voluptuous as vol
from homeassistant.const import ATTR_CODE, CONF_CODE, CONF_TOKEN, CONF_USERNAME from homeassistant.const import ATTR_CODE, CONF_CODE, CONF_TOKEN, CONF_USERNAME
@ -25,10 +14,6 @@ from homeassistant.helpers import (
config_validation as cv, config_validation as cv,
device_registry as dr, device_registry as dr,
) )
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
async_dispatcher_send,
)
from homeassistant.helpers.service import ( from homeassistant.helpers.service import (
async_register_admin_service, async_register_admin_service,
verify_domain_control, verify_domain_control,
@ -57,9 +42,7 @@ from .const import (
) )
DATA_LISTENER = "listener" DATA_LISTENER = "listener"
TOPIC_UPDATE_WEBSOCKET = "simplisafe_update_websocket_{0}"
EVENT_SIMPLISAFE_EVENT = "SIMPLISAFE_EVENT"
EVENT_SIMPLISAFE_NOTIFICATION = "SIMPLISAFE_NOTIFICATION" EVENT_SIMPLISAFE_NOTIFICATION = "SIMPLISAFE_NOTIFICATION"
DEFAULT_SOCKET_MIN_RETRY = 15 DEFAULT_SOCKET_MIN_RETRY = 15
@ -71,23 +54,7 @@ PLATFORMS = (
"sensor", "sensor",
) )
WEBSOCKET_EVENTS_REQUIRING_SERIAL = [EVENT_LOCK_LOCKED, EVENT_LOCK_UNLOCKED]
WEBSOCKET_EVENTS_TO_TRIGGER_HASS_EVENT = [
EVENT_CAMERA_MOTION_DETECTED,
EVENT_DOORBELL_DETECTED,
EVENT_ENTRY_DELAY,
EVENT_SECRET_ALERT_TRIGGERED,
]
ATTR_CATEGORY = "category" ATTR_CATEGORY = "category"
ATTR_LAST_EVENT_CHANGED_BY = "last_event_changed_by"
ATTR_LAST_EVENT_INFO = "last_event_info"
ATTR_LAST_EVENT_SENSOR_NAME = "last_event_sensor_name"
ATTR_LAST_EVENT_SENSOR_SERIAL = "last_event_sensor_serial"
ATTR_LAST_EVENT_SENSOR_TYPE = "last_event_sensor_type"
ATTR_LAST_EVENT_TIMESTAMP = "last_event_timestamp"
ATTR_LAST_EVENT_TYPE = "last_event_type"
ATTR_LAST_EVENT_TYPE = "last_event_type"
ATTR_MESSAGE = "message" ATTR_MESSAGE = "message"
ATTR_PIN_LABEL = "label" ATTR_PIN_LABEL = "label"
ATTR_PIN_LABEL_OR_VALUE = "label_or_pin" ATTR_PIN_LABEL_OR_VALUE = "label_or_pin"
@ -337,66 +304,6 @@ async def async_reload_entry(hass, config_entry):
await hass.config_entries.async_reload(config_entry.entry_id) await hass.config_entries.async_reload(config_entry.entry_id)
class SimpliSafeWebsocket:
"""Define a SimpliSafe websocket "manager" object."""
def __init__(self, hass, websocket):
"""Initialize."""
self._hass = hass
self._websocket = websocket
@staticmethod
def _on_connect():
"""Define a handler to fire when the websocket is connected."""
LOGGER.info("Connected to websocket")
@staticmethod
def _on_disconnect():
"""Define a handler to fire when the websocket is disconnected."""
LOGGER.info("Disconnected from websocket")
def _on_event(self, event):
"""Define a handler to fire when a new SimpliSafe event arrives."""
LOGGER.debug("New websocket event: %s", event)
async_dispatcher_send(
self._hass, TOPIC_UPDATE_WEBSOCKET.format(event.system_id), event
)
if event.event_type not in WEBSOCKET_EVENTS_TO_TRIGGER_HASS_EVENT:
return
if event.sensor_type:
sensor_type = event.sensor_type.name
else:
sensor_type = None
self._hass.bus.async_fire(
EVENT_SIMPLISAFE_EVENT,
event_data={
ATTR_LAST_EVENT_CHANGED_BY: event.changed_by,
ATTR_LAST_EVENT_TYPE: event.event_type,
ATTR_LAST_EVENT_INFO: event.info,
ATTR_LAST_EVENT_SENSOR_NAME: event.sensor_name,
ATTR_LAST_EVENT_SENSOR_SERIAL: event.sensor_serial,
ATTR_LAST_EVENT_SENSOR_TYPE: sensor_type,
ATTR_SYSTEM_ID: event.system_id,
ATTR_LAST_EVENT_TIMESTAMP: event.timestamp,
},
)
async def async_connect(self):
"""Register handlers and connect to the websocket."""
self._websocket.on_connect(self._on_connect)
self._websocket.on_disconnect(self._on_disconnect)
self._websocket.on_event(self._on_event)
await self._websocket.async_connect()
async def async_disconnect(self):
"""Disconnect from the websocket."""
await self._websocket.async_disconnect()
class SimpliSafe: class SimpliSafe:
"""Define a SimpliSafe data object.""" """Define a SimpliSafe data object."""
@ -408,9 +315,7 @@ class SimpliSafe:
self._system_notifications = {} self._system_notifications = {}
self.config_entry = config_entry self.config_entry = config_entry
self.coordinator = None self.coordinator = None
self.initial_event_to_use = {}
self.systems = {} self.systems = {}
self.websocket = SimpliSafeWebsocket(hass, api.websocket)
@callback @callback
def _async_process_new_notifications(self, system): def _async_process_new_notifications(self, system):
@ -451,22 +356,6 @@ class SimpliSafe:
async def async_init(self): async def async_init(self):
"""Initialize the data class.""" """Initialize the data class."""
# 2021-04-29: Disabling connection to the websocket due to the SimpliSafe cloud
# removing it (and not providing a clear alternative).
# asyncio.create_task(self.websocket.async_connect())
# async def async_websocket_disconnect(_):
# """Define an event handler to disconnect from the websocket."""
# await self.websocket.async_disconnect()
# 2021-04-29: Disabling disconnection from the websocket due to the SimpliSafe
# cloud removing it (and not providing a clear alternative).
# self._hass.data[DOMAIN][DATA_LISTENER][self.config_entry.entry_id].append(
# self._hass.bus.async_listen_once(
# EVENT_HOMEASSISTANT_STOP, async_websocket_disconnect
# )
# )
self.systems = await self._api.get_systems() self.systems = await self._api.get_systems()
for system in self.systems.values(): for system in self.systems.values():
self._system_notifications[system.system_id] = set() self._system_notifications[system.system_id] = set()
@ -477,17 +366,6 @@ class SimpliSafe:
) )
) )
# Future events will come from the websocket, but since subscription to the
# websocket doesn't provide the most recent event, we grab it from the REST
# API to ensure event-related attributes aren't empty on startup:
try:
self.initial_event_to_use[
system.system_id
] = await system.get_latest_event()
except SimplipyError as err:
LOGGER.error("Error while fetching initial event: %s", err)
self.initial_event_to_use[system.system_id] = {}
self.coordinator = DataUpdateCoordinator( self.coordinator = DataUpdateCoordinator(
self._hass, self._hass,
LOGGER, LOGGER,
@ -557,36 +435,13 @@ class SimpliSafeEntity(CoordinatorEntity):
self._online = True self._online = True
self._simplisafe = simplisafe self._simplisafe = simplisafe
self._system = system self._system = system
self.websocket_events_to_listen_for = [
EVENT_CONNECTION_LOST,
EVENT_CONNECTION_RESTORED,
]
if serial: if serial:
self._serial = serial self._serial = serial
else: else:
self._serial = system.serial self._serial = system.serial
try: self._attrs = {ATTR_SYSTEM_ID: system.system_id}
sensor_type = EntityTypes(
simplisafe.initial_event_to_use[system.system_id].get("sensorType")
)
except ValueError:
sensor_type = EntityTypes.unknown
self._attrs = {
ATTR_LAST_EVENT_INFO: simplisafe.initial_event_to_use[system.system_id].get(
"info"
),
ATTR_LAST_EVENT_SENSOR_NAME: simplisafe.initial_event_to_use[
system.system_id
].get("sensorName"),
ATTR_LAST_EVENT_SENSOR_TYPE: sensor_type.name,
ATTR_LAST_EVENT_TIMESTAMP: simplisafe.initial_event_to_use[
system.system_id
].get("eventTimestamp"),
ATTR_SYSTEM_ID: system.system_id,
}
self._device_info = { self._device_info = {
"identifiers": {(DOMAIN, system.system_id)}, "identifiers": {(DOMAIN, system.system_id)},
@ -626,76 +481,15 @@ class SimpliSafeEntity(CoordinatorEntity):
"""Return the unique ID of the entity.""" """Return the unique ID of the entity."""
return self._serial return self._serial
@callback
def _async_internal_update_from_websocket_event(self, event):
"""Perform internal websocket handling prior to handing off."""
if event.event_type == EVENT_CONNECTION_LOST:
self._online = False
elif event.event_type == EVENT_CONNECTION_RESTORED:
self._online = True
# It's uncertain whether SimpliSafe events will still propagate down the
# websocket when the base station is offline. Just in case, we guard against
# further action until connection is restored:
if not self._online:
return
if event.sensor_type:
sensor_type = event.sensor_type.name
else:
sensor_type = None
self._attrs.update(
{
ATTR_LAST_EVENT_INFO: event.info,
ATTR_LAST_EVENT_SENSOR_NAME: event.sensor_name,
ATTR_LAST_EVENT_SENSOR_TYPE: sensor_type,
ATTR_LAST_EVENT_TIMESTAMP: event.timestamp,
}
)
self.async_update_from_websocket_event(event)
@callback @callback
def _handle_coordinator_update(self): def _handle_coordinator_update(self):
"""Update the entity with new REST API data.""" """Update the entity with new REST API data."""
self.async_update_from_rest_api() self.async_update_from_rest_api()
self.async_write_ha_state() self.async_write_ha_state()
@callback
def _handle_websocket_update(self, event):
"""Update the entity with new websocket data."""
# Ignore this event if it belongs to a system other than this one:
if event.system_id != self._system.system_id:
return
# Ignore this event if this entity hasn't expressed interest in its type:
if event.event_type not in self.websocket_events_to_listen_for:
return
# Ignore this event if it belongs to a entity with a different serial
# number from this one's:
if (
event.event_type in WEBSOCKET_EVENTS_REQUIRING_SERIAL
and event.sensor_serial != self._serial
):
return
self._async_internal_update_from_websocket_event(event)
self.async_write_ha_state()
async def async_added_to_hass(self): async def async_added_to_hass(self):
"""Register callbacks.""" """Register callbacks."""
await super().async_added_to_hass() await super().async_added_to_hass()
self.async_on_remove(
async_dispatcher_connect(
self.hass,
TOPIC_UPDATE_WEBSOCKET.format(self._system.system_id),
self._handle_websocket_update,
)
)
self.async_update_from_rest_api() self.async_update_from_rest_api()
@callback @callback
@ -703,10 +497,6 @@ class SimpliSafeEntity(CoordinatorEntity):
"""Update the entity with the provided REST API data.""" """Update the entity with the provided REST API data."""
raise NotImplementedError() raise NotImplementedError()
@callback
def async_update_from_websocket_event(self, event):
"""Update the entity with the provided websocket event."""
class SimpliSafeBaseSensor(SimpliSafeEntity): class SimpliSafeBaseSensor(SimpliSafeEntity):
"""Define a SimpliSafe base (binary) sensor.""" """Define a SimpliSafe base (binary) sensor."""

View File

@ -3,19 +3,6 @@ import re
from simplipy.errors import SimplipyError from simplipy.errors import SimplipyError
from simplipy.system import SystemStates from simplipy.system import SystemStates
from simplipy.websocket import (
EVENT_ALARM_CANCELED,
EVENT_ALARM_TRIGGERED,
EVENT_ARMED_AWAY,
EVENT_ARMED_AWAY_BY_KEYPAD,
EVENT_ARMED_AWAY_BY_REMOTE,
EVENT_ARMED_HOME,
EVENT_AWAY_EXIT_DELAY_BY_KEYPAD,
EVENT_AWAY_EXIT_DELAY_BY_REMOTE,
EVENT_DISARMED_BY_MASTER_PIN,
EVENT_DISARMED_BY_REMOTE,
EVENT_HOME_EXIT_DELAY,
)
from homeassistant.components.alarm_control_panel import ( from homeassistant.components.alarm_control_panel import (
FORMAT_NUMBER, FORMAT_NUMBER,
@ -96,21 +83,6 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanelEntity):
else: else:
self._state = None self._state = None
for event_type in (
EVENT_ALARM_CANCELED,
EVENT_ALARM_TRIGGERED,
EVENT_ARMED_AWAY,
EVENT_ARMED_AWAY_BY_KEYPAD,
EVENT_ARMED_AWAY_BY_REMOTE,
EVENT_ARMED_HOME,
EVENT_AWAY_EXIT_DELAY_BY_KEYPAD,
EVENT_AWAY_EXIT_DELAY_BY_REMOTE,
EVENT_DISARMED_BY_MASTER_PIN,
EVENT_DISARMED_BY_REMOTE,
EVENT_HOME_EXIT_DELAY,
):
self.websocket_events_to_listen_for.append(event_type)
@property @property
def changed_by(self): def changed_by(self):
"""Return info about who changed the alarm last.""" """Return info about who changed the alarm last."""
@ -237,33 +209,3 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanelEntity):
self._state = STATE_ALARM_DISARMED self._state = STATE_ALARM_DISARMED
else: else:
self._state = None self._state = None
@callback
def async_update_from_websocket_event(self, event):
"""Update the entity with the provided websocket API event data."""
if event.event_type in (
EVENT_ALARM_CANCELED,
EVENT_DISARMED_BY_MASTER_PIN,
EVENT_DISARMED_BY_REMOTE,
):
self._state = STATE_ALARM_DISARMED
elif event.event_type == EVENT_ALARM_TRIGGERED:
self._state = STATE_ALARM_TRIGGERED
elif event.event_type in (
EVENT_ARMED_AWAY,
EVENT_ARMED_AWAY_BY_KEYPAD,
EVENT_ARMED_AWAY_BY_REMOTE,
):
self._state = STATE_ALARM_ARMED_AWAY
elif event.event_type == EVENT_ARMED_HOME:
self._state = STATE_ALARM_ARMED_HOME
elif event.event_type in (
EVENT_AWAY_EXIT_DELAY_BY_KEYPAD,
EVENT_AWAY_EXIT_DELAY_BY_REMOTE,
EVENT_HOME_EXIT_DELAY,
):
self._state = STATE_ALARM_ARMING
else:
self._state = None
self._changed_by = event.changed_by

View File

@ -1,7 +1,6 @@
"""Support for SimpliSafe locks.""" """Support for SimpliSafe locks."""
from simplipy.errors import SimplipyError from simplipy.errors import SimplipyError
from simplipy.lock import LockStates from simplipy.lock import LockStates
from simplipy.websocket import EVENT_LOCK_LOCKED, EVENT_LOCK_UNLOCKED
from homeassistant.components.lock import LockEntity from homeassistant.components.lock import LockEntity
from homeassistant.core import callback from homeassistant.core import callback
@ -39,9 +38,6 @@ class SimpliSafeLock(SimpliSafeEntity, LockEntity):
self._lock = lock self._lock = lock
self._is_locked = None self._is_locked = None
for event_type in (EVENT_LOCK_LOCKED, EVENT_LOCK_UNLOCKED):
self.websocket_events_to_listen_for.append(event_type)
@property @property
def is_locked(self): def is_locked(self):
"""Return true if the lock is locked.""" """Return true if the lock is locked."""
@ -81,11 +77,3 @@ class SimpliSafeLock(SimpliSafeEntity, LockEntity):
) )
self._is_locked = self._lock.state == LockStates.locked self._is_locked = self._lock.state == LockStates.locked
@callback
def async_update_from_websocket_event(self, event):
"""Update the entity with the provided websocket event data."""
if event.event_type == EVENT_LOCK_LOCKED:
self._is_locked = True
else:
self._is_locked = False

View File

@ -3,7 +3,7 @@
"name": "SimpliSafe", "name": "SimpliSafe",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/simplisafe", "documentation": "https://www.home-assistant.io/integrations/simplisafe",
"requirements": ["simplisafe-python==9.6.10"], "requirements": ["simplisafe-python==10.0.0"],
"codeowners": ["@bachya"], "codeowners": ["@bachya"],
"iot_class": "cloud_polling" "iot_class": "cloud_polling"
} }

View File

@ -2067,7 +2067,7 @@ simplehound==0.3
simplepush==1.1.4 simplepush==1.1.4
# homeassistant.components.simplisafe # homeassistant.components.simplisafe
simplisafe-python==9.6.10 simplisafe-python==10.0.0
# homeassistant.components.sisyphus # homeassistant.components.sisyphus
sisyphus-control==3.0 sisyphus-control==3.0

View File

@ -1101,7 +1101,7 @@ sharkiqpy==0.1.8
simplehound==0.3 simplehound==0.3
# homeassistant.components.simplisafe # homeassistant.components.simplisafe
simplisafe-python==9.6.10 simplisafe-python==10.0.0
# homeassistant.components.slack # homeassistant.components.slack
slackclient==2.5.0 slackclient==2.5.0