mirror of
https://github.com/home-assistant/core.git
synced 2025-08-14 01:55:18 +02:00
Websovket dataclass and other code review
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
"""Support for SimpliSafe alarm systems."""
|
"""Support for SimpliSafe alarm systems."""
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from datetime import datetime
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from simplipy import API
|
from simplipy import API
|
||||||
from simplipy.entity import EntityTypes
|
from simplipy.entity import EntityTypes
|
||||||
@@ -38,11 +41,6 @@ from .const import (
|
|||||||
ATTR_ENTRY_DELAY_HOME,
|
ATTR_ENTRY_DELAY_HOME,
|
||||||
ATTR_EXIT_DELAY_AWAY,
|
ATTR_EXIT_DELAY_AWAY,
|
||||||
ATTR_EXIT_DELAY_HOME,
|
ATTR_EXIT_DELAY_HOME,
|
||||||
ATTR_LAST_EVENT_INFO,
|
|
||||||
ATTR_LAST_EVENT_SENSOR_NAME,
|
|
||||||
ATTR_LAST_EVENT_SENSOR_TYPE,
|
|
||||||
ATTR_LAST_EVENT_TIMESTAMP,
|
|
||||||
ATTR_LAST_EVENT_TYPE,
|
|
||||||
ATTR_LIGHT,
|
ATTR_LIGHT,
|
||||||
ATTR_VOICE_PROMPT_VOLUME,
|
ATTR_VOICE_PROMPT_VOLUME,
|
||||||
DATA_CLIENT,
|
DATA_CLIENT,
|
||||||
@@ -61,6 +59,10 @@ TOPIC_UPDATE = "simplisafe_update_data_{0}"
|
|||||||
DEFAULT_SOCKET_MIN_RETRY = 15
|
DEFAULT_SOCKET_MIN_RETRY = 15
|
||||||
DEFAULT_WATCHDOG_SECONDS = 5 * 60
|
DEFAULT_WATCHDOG_SECONDS = 5 * 60
|
||||||
|
|
||||||
|
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_PIN_LABEL = "label"
|
ATTR_PIN_LABEL = "label"
|
||||||
ATTR_PIN_LABEL_OR_VALUE = "label_or_pin"
|
ATTR_PIN_LABEL_OR_VALUE = "label_or_pin"
|
||||||
ATTR_PIN_VALUE = "pin"
|
ATTR_PIN_VALUE = "pin"
|
||||||
@@ -131,37 +133,6 @@ def _async_save_refresh_token(hass, config_entry, token):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_create_event_from_raw_data(event_data):
|
|
||||||
"""Create a generated payload from raw event data."""
|
|
||||||
event_type = get_event_type_from_payload(event_data)
|
|
||||||
|
|
||||||
# simplisafe-python will take care of logging a message if it finds an event
|
|
||||||
# type it doesn't know about, so if get_event_type_from_payload() returns
|
|
||||||
# None, just return:
|
|
||||||
if not event_type:
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
event_sensor_type = EntityTypes(event_data["sensorType"]).name
|
|
||||||
except ValueError:
|
|
||||||
_LOGGER.warning(
|
|
||||||
'Encountered unknown entity type: %s ("%s"). Please report it at'
|
|
||||||
"https://github.com/home-assistant/home-assistant/issues.",
|
|
||||||
event_data["sensorType"],
|
|
||||||
event_data["sensorName"],
|
|
||||||
)
|
|
||||||
event_sensor_type = None
|
|
||||||
|
|
||||||
return {
|
|
||||||
ATTR_LAST_EVENT_INFO: event_data["info"],
|
|
||||||
ATTR_LAST_EVENT_SENSOR_NAME: event_data["sensorName"],
|
|
||||||
ATTR_LAST_EVENT_SENSOR_TYPE: event_sensor_type,
|
|
||||||
ATTR_LAST_EVENT_TIMESTAMP: utc_from_timestamp(event_data["eventTimestamp"]),
|
|
||||||
ATTR_LAST_EVENT_TYPE: event_type,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def async_register_base_station(hass, system, config_entry_id):
|
async def async_register_base_station(hass, system, config_entry_id):
|
||||||
"""Register a new bridge."""
|
"""Register a new bridge."""
|
||||||
device_registry = await dr.async_get_registry(hass)
|
device_registry = await dr.async_get_registry(hass)
|
||||||
@@ -328,27 +299,62 @@ async def async_unload_entry(hass, entry):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class SimpliSafe:
|
@dataclass(frozen=True)
|
||||||
"""Define a SimpliSafe API object."""
|
class SimpliSafeWebsocketEvent:
|
||||||
|
"""Define a representation of a parsed websocket event."""
|
||||||
|
|
||||||
def __init__(self, hass, api, config_entry):
|
event_data: dict
|
||||||
|
|
||||||
|
changed_by: Optional[str] = field(init=False)
|
||||||
|
event_type: Optional[str] = field(init=False)
|
||||||
|
info: str = field(init=False)
|
||||||
|
sensor_name: str = field(init=False)
|
||||||
|
sensor_type: EntityTypes = field(init=False)
|
||||||
|
timestamp: datetime = field(init=False)
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
"""Initialize."""
|
||||||
|
object.__setattr__(self, "changed_by", self.event_data["pinName"])
|
||||||
|
object.__setattr__(
|
||||||
|
self, "event_type", get_event_type_from_payload(self.event_data)
|
||||||
|
)
|
||||||
|
object.__setattr__(self, "info", self.event_data["info"])
|
||||||
|
object.__setattr__(self, "sensor_name", self.event_data["sensorName"])
|
||||||
|
object.__setattr__(
|
||||||
|
self, "timestamp", utc_from_timestamp(self.event_data["eventTimestamp"])
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
object.__setattr__(
|
||||||
|
self, "sensor_type", EntityTypes(self.event_data["sensorType"]).name
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
_LOGGER.warning(
|
||||||
|
'Encountered unknown entity type: %s ("%s"). Please report it at'
|
||||||
|
"https://github.com/home-assistant/home-assistant/issues.",
|
||||||
|
self.event_data["sensorType"],
|
||||||
|
self.event_data["sensorName"],
|
||||||
|
)
|
||||||
|
object.__setattr__(self, "sensor_type", None)
|
||||||
|
|
||||||
|
|
||||||
|
class SimpliSafeWebsocket:
|
||||||
|
"""Define a SimpliSafe websocket "manager" object."""
|
||||||
|
|
||||||
|
def __init__(self, hass, websocket):
|
||||||
"""Initialize."""
|
"""Initialize."""
|
||||||
self._api = api
|
|
||||||
self._config_entry = config_entry
|
|
||||||
self._emergency_refresh_token_used = False
|
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
|
self._websocket = websocket
|
||||||
self._websocket_reconnect_delay = DEFAULT_SOCKET_MIN_RETRY
|
self._websocket_reconnect_delay = DEFAULT_SOCKET_MIN_RETRY
|
||||||
self._websocket_watchdog_listener = None
|
self._websocket_watchdog_listener = None
|
||||||
self.last_rest_api_data = {}
|
self.last_events = {}
|
||||||
self.last_websocket_data = {}
|
|
||||||
self.systems = None
|
|
||||||
|
|
||||||
hass.loop.create_task(self.async_websocket_connect())
|
hass.loop.create_task(self.async_websocket_connect())
|
||||||
|
|
||||||
async def _attempt_websocket_connect(self):
|
async def _async_attempt_websocket_connect(self):
|
||||||
"""Attempt to connect to the websocket (retrying later on fail)."""
|
"""Attempt to connect to the websocket (retrying later on fail)."""
|
||||||
try:
|
try:
|
||||||
await self._api.websocket.async_connect()
|
await self._websocket.async_connect()
|
||||||
except WebsocketError as err:
|
except WebsocketError as err:
|
||||||
_LOGGER.error("Error with the websocket connection: %s", err)
|
_LOGGER.error("Error with the websocket connection: %s", err)
|
||||||
self._websocket_reconnect_delay = min(
|
self._websocket_reconnect_delay = min(
|
||||||
@@ -360,11 +366,70 @@ class SimpliSafe:
|
|||||||
self.async_websocket_connect,
|
self.async_websocket_connect,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def _async_websocket_reconnect(self, event_time):
|
||||||
|
"""Forcibly disconnect from and reconnect to the websocket."""
|
||||||
|
_LOGGER.debug("Websocket watchdog expired; forcing socket reconnection")
|
||||||
|
await self.async_websocket_disconnect()
|
||||||
|
await self._async_attempt_websocket_connect()
|
||||||
|
|
||||||
|
async def async_websocket_connect(self):
|
||||||
|
"""Register handlers and connect to the websocket."""
|
||||||
|
|
||||||
|
def on_connect():
|
||||||
|
"""Define a handler to fire when the websocket is connected."""
|
||||||
|
_LOGGER.info("Connected to websocket")
|
||||||
|
_LOGGER.debug("Websocket watchdog starting")
|
||||||
|
if self._websocket_watchdog_listener is not None:
|
||||||
|
self._websocket_watchdog_listener()
|
||||||
|
self._websocket_watchdog_listener = async_call_later(
|
||||||
|
self._hass, DEFAULT_WATCHDOG_SECONDS, self._async_websocket_reconnect
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_disconnect():
|
||||||
|
"""Define a handler to fire when the websocket is disconnected."""
|
||||||
|
_LOGGER.info("Disconnected from websocket")
|
||||||
|
|
||||||
|
def on_event(data):
|
||||||
|
"""Define a handler to fire when a new SimpliSafe event arrives."""
|
||||||
|
event = SimpliSafeWebsocketEvent(data)
|
||||||
|
_LOGGER.debug("New websocket event: %s", event)
|
||||||
|
self.last_events[data["sid"]] = event
|
||||||
|
async_dispatcher_send(self._hass, TOPIC_UPDATE.format(data["sid"]))
|
||||||
|
|
||||||
|
_LOGGER.debug("Resetting websocket watchdog")
|
||||||
|
self._websocket_watchdog_listener()
|
||||||
|
self._websocket_watchdog_listener = async_call_later(
|
||||||
|
self._hass, DEFAULT_WATCHDOG_SECONDS, self._async_websocket_reconnect
|
||||||
|
)
|
||||||
|
self._websocket_reconnect_delay = DEFAULT_SOCKET_MIN_RETRY
|
||||||
|
|
||||||
|
self._websocket.on_connect(on_connect)
|
||||||
|
self._websocket.on_disconnect(on_disconnect)
|
||||||
|
self._websocket.on_event(on_event)
|
||||||
|
|
||||||
|
await self._async_attempt_websocket_connect()
|
||||||
|
|
||||||
|
async def async_websocket_disconnect(self):
|
||||||
|
"""Disconnect from the websocket."""
|
||||||
|
await self._websocket.async_disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
class SimpliSafe:
|
||||||
|
"""Define a SimpliSafe data object."""
|
||||||
|
|
||||||
|
def __init__(self, hass, api, config_entry):
|
||||||
|
"""Initialize."""
|
||||||
|
self._api = api
|
||||||
|
self._config_entry = config_entry
|
||||||
|
self._emergency_refresh_token_used = False
|
||||||
|
self._hass = hass
|
||||||
|
self.inital_event_to_use = {}
|
||||||
|
self.systems = None
|
||||||
|
self.websocket = SimpliSafeWebsocket(hass, api.websocket)
|
||||||
|
|
||||||
async def async_init(self):
|
async def async_init(self):
|
||||||
"""Initialize the data class."""
|
"""Initialize the data class."""
|
||||||
self.systems = await self._api.get_systems()
|
self.systems = await self._api.get_systems()
|
||||||
|
|
||||||
# Register the base station for each system:
|
|
||||||
for system in self.systems.values():
|
for system in self.systems.values():
|
||||||
self._hass.async_create_task(
|
self._hass.async_create_task(
|
||||||
async_register_base_station(
|
async_register_base_station(
|
||||||
@@ -380,6 +445,17 @@ class SimpliSafe:
|
|||||||
self._config_entry.entry_id
|
self._config_entry.entry_id
|
||||||
] = async_track_time_interval(self._hass, refresh, DEFAULT_SCAN_INTERVAL)
|
] = async_track_time_interval(self._hass, refresh, DEFAULT_SCAN_INTERVAL)
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
most_recent_event = await system.get_latest_event()
|
||||||
|
except SimplipyError as err:
|
||||||
|
_LOGGER.error("Error while fetching initial event: %s", err)
|
||||||
|
self.inital_event_to_use[system.system_id] = None
|
||||||
|
else:
|
||||||
|
self.inital_event_to_use[system.system_id] = most_recent_event
|
||||||
|
|
||||||
await self.async_update()
|
await self.async_update()
|
||||||
|
|
||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
@@ -388,7 +464,7 @@ class SimpliSafe:
|
|||||||
async def update_system(system):
|
async def update_system(system):
|
||||||
"""Update a system."""
|
"""Update a system."""
|
||||||
await system.update()
|
await system.update()
|
||||||
_LOGGER.debug('Updated REST API data for "%s"', system.name)
|
_LOGGER.debug('Updated REST API data for "%s"', system.address)
|
||||||
async_dispatcher_send(self._hass, TOPIC_UPDATE.format(system.system_id))
|
async_dispatcher_send(self._hass, TOPIC_UPDATE.format(system.system_id))
|
||||||
|
|
||||||
tasks = [update_system(system) for system in self.systems.values()]
|
tasks = [update_system(system) for system in self.systems.values()]
|
||||||
@@ -437,66 +513,17 @@ class SimpliSafe:
|
|||||||
if self._emergency_refresh_token_used:
|
if self._emergency_refresh_token_used:
|
||||||
self._emergency_refresh_token_used = False
|
self._emergency_refresh_token_used = False
|
||||||
|
|
||||||
async def async_websocket_connect(self):
|
|
||||||
"""Register handlers and connect to the websocket."""
|
|
||||||
|
|
||||||
async def _websocket_reconnect(event_time):
|
|
||||||
"""Forcibly disconnect from and reconnect to the websocket."""
|
|
||||||
_LOGGER.debug("Websocket watchdog expired; forcing socket reconnection")
|
|
||||||
await self._api.websocket.async_disconnect()
|
|
||||||
await self._attempt_websocket_connect()
|
|
||||||
|
|
||||||
def on_connect():
|
|
||||||
"""Define a handler to fire when the websocket is connected."""
|
|
||||||
_LOGGER.info("Connected to websocket")
|
|
||||||
_LOGGER.debug("Websocket watchdog starting")
|
|
||||||
if self._websocket_watchdog_listener is not None:
|
|
||||||
self._websocket_watchdog_listener()
|
|
||||||
self._websocket_watchdog_listener = async_call_later(
|
|
||||||
self._hass, DEFAULT_WATCHDOG_SECONDS, _websocket_reconnect
|
|
||||||
)
|
|
||||||
|
|
||||||
def on_disconnect():
|
|
||||||
"""Define a handler to fire when the websocket is disconnected."""
|
|
||||||
_LOGGER.info("Disconnected from websocket")
|
|
||||||
|
|
||||||
def on_event(data):
|
|
||||||
"""Define a handler to fire when a new SimpliSafe event arrives."""
|
|
||||||
event = async_create_event_from_raw_data(data)
|
|
||||||
self.last_websocket_data[data["sid"]] = event
|
|
||||||
system = self.systems[data["sid"]]
|
|
||||||
_LOGGER.debug('Updated websocket data for "%s"', system.name)
|
|
||||||
async_dispatcher_send(self._hass, TOPIC_UPDATE.format(data["sid"]))
|
|
||||||
|
|
||||||
_LOGGER.debug("Resetting websocket watchdog")
|
|
||||||
self._websocket_watchdog_listener()
|
|
||||||
self._websocket_watchdog_listener = async_call_later(
|
|
||||||
self._hass, DEFAULT_WATCHDOG_SECONDS, _websocket_reconnect
|
|
||||||
)
|
|
||||||
self._websocket_reconnect_delay = DEFAULT_SOCKET_MIN_RETRY
|
|
||||||
|
|
||||||
self._api.websocket.on_connect(on_connect)
|
|
||||||
self._api.websocket.on_disconnect(on_disconnect)
|
|
||||||
self._api.websocket.on_event(on_event)
|
|
||||||
|
|
||||||
await self._attempt_websocket_connect()
|
|
||||||
|
|
||||||
async def async_websocket_disconnect(self):
|
|
||||||
"""Disconnect from the websocket."""
|
|
||||||
await self._api.websocket.async_disconnect()
|
|
||||||
|
|
||||||
|
|
||||||
class SimpliSafeEntity(Entity):
|
class SimpliSafeEntity(Entity):
|
||||||
"""Define a base SimpliSafe entity."""
|
"""Define a base SimpliSafe entity."""
|
||||||
|
|
||||||
def __init__(self, system, name, *, serial=None):
|
def __init__(self, simplisafe, system, name, *, serial=None):
|
||||||
"""Initialize."""
|
"""Initialize."""
|
||||||
self._async_unsub_dispatcher_connect = None
|
self._async_unsub_dispatcher_connect = None
|
||||||
self._attrs = {ATTR_SYSTEM_ID: system.system_id}
|
self._last_used_websocket_event = None
|
||||||
self._last_used_rest_api_data = None
|
|
||||||
self._last_used_websocket_data = None
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self._online = True
|
self._online = True
|
||||||
|
self._simplisafe = simplisafe
|
||||||
self._state = None
|
self._state = None
|
||||||
self._system = system
|
self._system = system
|
||||||
|
|
||||||
@@ -505,6 +532,22 @@ class SimpliSafeEntity(Entity):
|
|||||||
else:
|
else:
|
||||||
self._serial = system.serial
|
self._serial = system.serial
|
||||||
|
|
||||||
|
self._attrs = {
|
||||||
|
ATTR_LAST_EVENT_INFO: simplisafe.inital_event_to_use[system.system_id][
|
||||||
|
"info"
|
||||||
|
],
|
||||||
|
ATTR_LAST_EVENT_SENSOR_NAME: simplisafe.inital_event_to_use[
|
||||||
|
system.system_id
|
||||||
|
]["sensorName"],
|
||||||
|
ATTR_LAST_EVENT_SENSOR_TYPE: simplisafe.inital_event_to_use[
|
||||||
|
system.system_id
|
||||||
|
]["sensorType"],
|
||||||
|
ATTR_LAST_EVENT_TIMESTAMP: simplisafe.inital_event_to_use[system.system_id][
|
||||||
|
"eventTimestamp"
|
||||||
|
],
|
||||||
|
ATTR_SYSTEM_ID: system.system_id,
|
||||||
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self):
|
def available(self):
|
||||||
"""Return whether the entity is available."""
|
"""Return whether the entity is available."""
|
||||||
@@ -555,28 +598,36 @@ class SimpliSafeEntity(Entity):
|
|||||||
|
|
||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
"""Update the entity."""
|
"""Update the entity."""
|
||||||
rest_data = self.last_rest_api_data.get(self._system.system_id)
|
self.async_update_from_rest_api()
|
||||||
websocket_data = self.last_websocket_data.get(self._system.system_id)
|
|
||||||
|
|
||||||
# If the most recent REST API data (within the data object) doesn't match what
|
# Since the REST API triggers this coroutine, we don't want old websocket events
|
||||||
# this entity last used, update:
|
# to unnecessarily overwrite things; so, we return if the last websocket event
|
||||||
if self._last_used_rest_api_data != rest_data:
|
# is one the entity has already responded to:
|
||||||
self._last_used_rest_api_data = rest_data
|
last_websocket_event = self._simplisafe.websocket.last_events.get(
|
||||||
self.async_update_from_rest_api(rest_data)
|
self._system.system_id
|
||||||
|
)
|
||||||
|
|
||||||
# If the most recent websocket data (within the data object) doesn't match what
|
if self._last_used_websocket_event == last_websocket_event:
|
||||||
# this entity last used, update:
|
return
|
||||||
if self._last_used_websocket_data != websocket_data:
|
|
||||||
self._last_used_websocket_data = websocket_data
|
self._last_used_websocket_event = last_websocket_event
|
||||||
self.async_update_from_websocket_api(websocket_data)
|
self._attrs.update(
|
||||||
|
{
|
||||||
|
ATTR_LAST_EVENT_INFO: last_websocket_event.info,
|
||||||
|
ATTR_LAST_EVENT_SENSOR_NAME: last_websocket_event.sensor_name,
|
||||||
|
ATTR_LAST_EVENT_SENSOR_TYPE: last_websocket_event.sensor_type,
|
||||||
|
ATTR_LAST_EVENT_TIMESTAMP: last_websocket_event.timestamp,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.async_update_from_websocket_event(last_websocket_event)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_from_rest_api(self, data):
|
def async_update_from_rest_api(self):
|
||||||
"""Update the entity with the provided REST API data."""
|
"""Update the entity with the provided REST API data."""
|
||||||
raise NotImplementedError()
|
pass
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_from_websocket_api(self, data):
|
def async_update_from_websocket_event(self, event):
|
||||||
"""Update the entity with the provided websocket API data."""
|
"""Update the entity with the provided websocket API data."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@@ -46,7 +46,6 @@ from .const import (
|
|||||||
ATTR_ENTRY_DELAY_HOME,
|
ATTR_ENTRY_DELAY_HOME,
|
||||||
ATTR_EXIT_DELAY_AWAY,
|
ATTR_EXIT_DELAY_AWAY,
|
||||||
ATTR_EXIT_DELAY_HOME,
|
ATTR_EXIT_DELAY_HOME,
|
||||||
ATTR_LAST_EVENT_TYPE,
|
|
||||||
ATTR_LIGHT,
|
ATTR_LIGHT,
|
||||||
ATTR_VOICE_PROMPT_VOLUME,
|
ATTR_VOICE_PROMPT_VOLUME,
|
||||||
DATA_CLIENT,
|
DATA_CLIENT,
|
||||||
@@ -81,11 +80,10 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanel):
|
|||||||
|
|
||||||
def __init__(self, simplisafe, system, code):
|
def __init__(self, simplisafe, system, code):
|
||||||
"""Initialize the SimpliSafe alarm."""
|
"""Initialize the SimpliSafe alarm."""
|
||||||
super().__init__(system, "Alarm Control Panel")
|
super().__init__(simplisafe, system, "Alarm Control Panel")
|
||||||
self._changed_by = None
|
self._changed_by = None
|
||||||
self._code = code
|
self._code = code
|
||||||
self._last_event = None
|
self._last_event = None
|
||||||
self._simplisafe = simplisafe
|
|
||||||
|
|
||||||
if system.alarm_going_off:
|
if system.alarm_going_off:
|
||||||
self._state = STATE_ALARM_TRIGGERED
|
self._state = STATE_ALARM_TRIGGERED
|
||||||
@@ -175,12 +173,11 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanel):
|
|||||||
self._state = STATE_ALARM_ARMING
|
self._state = STATE_ALARM_ARMING
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_from_rest_api(self, data):
|
def async_update_from_rest_api(self):
|
||||||
"""Update the entity with the provided REST API data."""
|
"""Update the entity with the provided REST API data."""
|
||||||
if self._system.state == SystemStates.error:
|
if self._system.state == SystemStates.error:
|
||||||
self._online = False
|
self._online = False
|
||||||
return
|
return
|
||||||
|
|
||||||
self._online = True
|
self._online = True
|
||||||
|
|
||||||
if self._system.version == 3:
|
if self._system.version == 3:
|
||||||
@@ -206,28 +203,25 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_from_websocket_api(self, data):
|
def async_update_from_websocket_event(self, event):
|
||||||
"""Update the entity with the provided websocket API data."""
|
"""Update the entity with the provided websocket API event data."""
|
||||||
if data.get(ATTR_PIN_NAME):
|
if event.event_type in (
|
||||||
self._changed_by = data[ATTR_PIN_NAME]
|
|
||||||
|
|
||||||
if data[ATTR_LAST_EVENT_TYPE] in (
|
|
||||||
EVENT_ALARM_CANCELED,
|
EVENT_ALARM_CANCELED,
|
||||||
EVENT_DISARMED_BY_MASTER_PIN,
|
EVENT_DISARMED_BY_MASTER_PIN,
|
||||||
EVENT_DISARMED_BY_REMOTE,
|
EVENT_DISARMED_BY_REMOTE,
|
||||||
):
|
):
|
||||||
self._state = STATE_ALARM_DISARMED
|
self._state = STATE_ALARM_DISARMED
|
||||||
elif data[ATTR_LAST_EVENT_TYPE] == EVENT_ALARM_TRIGGERED:
|
elif event.event_type == EVENT_ALARM_TRIGGERED:
|
||||||
self._state = STATE_ALARM_TRIGGERED
|
self._state = STATE_ALARM_TRIGGERED
|
||||||
elif data[ATTR_LAST_EVENT_TYPE] in (
|
elif event.event_type in (
|
||||||
EVENT_ARMED_AWAY,
|
EVENT_ARMED_AWAY,
|
||||||
EVENT_ARMED_AWAY_BY_KEYPAD,
|
EVENT_ARMED_AWAY_BY_KEYPAD,
|
||||||
EVENT_ARMED_AWAY_BY_REMOTE,
|
EVENT_ARMED_AWAY_BY_REMOTE,
|
||||||
):
|
):
|
||||||
self._state = STATE_ALARM_ARMED_AWAY
|
self._state = STATE_ALARM_ARMED_AWAY
|
||||||
elif data[ATTR_LAST_EVENT_TYPE] == EVENT_ARMED_HOME:
|
elif event.event_type == EVENT_ARMED_HOME:
|
||||||
self._state = STATE_ALARM_ARMED_HOME
|
self._state = STATE_ALARM_ARMED_HOME
|
||||||
elif data[ATTR_LAST_EVENT_TYPE] in (
|
elif event.event_type in (
|
||||||
EVENT_AWAY_EXIT_DELAY_BY_KEYPAD,
|
EVENT_AWAY_EXIT_DELAY_BY_KEYPAD,
|
||||||
EVENT_AWAY_EXIT_DELAY_BY_REMOTE,
|
EVENT_AWAY_EXIT_DELAY_BY_REMOTE,
|
||||||
EVENT_HOME_EXIT_DELAY,
|
EVENT_HOME_EXIT_DELAY,
|
||||||
@@ -236,4 +230,4 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanel):
|
|||||||
else:
|
else:
|
||||||
self._state = None
|
self._state = None
|
||||||
|
|
||||||
self._attrs.update(data)
|
self._changed_by = event.changed_by
|
||||||
|
@@ -16,11 +16,6 @@ ATTR_ENTRY_DELAY_AWAY = "entry_delay_away"
|
|||||||
ATTR_ENTRY_DELAY_HOME = "entry_delay_home"
|
ATTR_ENTRY_DELAY_HOME = "entry_delay_home"
|
||||||
ATTR_EXIT_DELAY_AWAY = "exit_delay_away"
|
ATTR_EXIT_DELAY_AWAY = "exit_delay_away"
|
||||||
ATTR_EXIT_DELAY_HOME = "exit_delay_home"
|
ATTR_EXIT_DELAY_HOME = "exit_delay_home"
|
||||||
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_LIGHT = "light"
|
ATTR_LIGHT = "light"
|
||||||
ATTR_VOICE_PROMPT_VOLUME = "voice_prompt_volume"
|
ATTR_VOICE_PROMPT_VOLUME = "voice_prompt_volume"
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@ from homeassistant.const import STATE_LOCKED, STATE_UNKNOWN, STATE_UNLOCKED
|
|||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
|
||||||
from . import SimpliSafeEntity
|
from . import SimpliSafeEntity
|
||||||
from .const import ATTR_LAST_EVENT_TYPE, DATA_CLIENT, DOMAIN
|
from .const import DATA_CLIENT, DOMAIN
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ async def async_setup_entry(hass, entry, async_add_entities):
|
|||||||
simplisafe = hass.data[DOMAIN][DATA_CLIENT][entry.entry_id]
|
simplisafe = hass.data[DOMAIN][DATA_CLIENT][entry.entry_id]
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
[
|
[
|
||||||
SimpliSafeLock(system, lock)
|
SimpliSafeLock(simplisafe, system, lock)
|
||||||
for system in simplisafe.systems.values()
|
for system in simplisafe.systems.values()
|
||||||
for lock in system.locks.values()
|
for lock in system.locks.values()
|
||||||
]
|
]
|
||||||
@@ -34,9 +34,9 @@ async def async_setup_entry(hass, entry, async_add_entities):
|
|||||||
class SimpliSafeLock(SimpliSafeEntity, LockDevice):
|
class SimpliSafeLock(SimpliSafeEntity, LockDevice):
|
||||||
"""Define a SimpliSafe lock."""
|
"""Define a SimpliSafe lock."""
|
||||||
|
|
||||||
def __init__(self, system, lock):
|
def __init__(self, simplisafe, system, lock):
|
||||||
"""Initialize."""
|
"""Initialize."""
|
||||||
super().__init__(system, lock.name, serial=lock.serial)
|
super().__init__(simplisafe, system, lock.name, serial=lock.serial)
|
||||||
self._lock = lock
|
self._lock = lock
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -65,7 +65,7 @@ class SimpliSafeLock(SimpliSafeEntity, LockDevice):
|
|||||||
self._state = STATE_UNLOCKED
|
self._state = STATE_UNLOCKED
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_from_rest_api(self, data):
|
def async_update_from_rest_api(self):
|
||||||
"""Update the entity with the provided REST API data."""
|
"""Update the entity with the provided REST API data."""
|
||||||
if self._lock.offline or self._lock.disabled:
|
if self._lock.offline or self._lock.disabled:
|
||||||
self._online = False
|
self._online = False
|
||||||
@@ -81,15 +81,13 @@ class SimpliSafeLock(SimpliSafeEntity, LockDevice):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_from_websocket_api(self, data):
|
def async_update_from_websocket_event(self, event):
|
||||||
"""Update the entity with the provided websocket API data."""
|
"""Update the entity with the provided websocket event data."""
|
||||||
if data[ATTR_LAST_EVENT_TYPE] == EVENT_LOCK_LOCKED:
|
if event.event_type == EVENT_LOCK_LOCKED:
|
||||||
self._state = STATE_LOCKED
|
self._state = STATE_LOCKED
|
||||||
elif data[ATTR_LAST_EVENT_TYPE] == EVENT_LOCK_UNLOCKED:
|
elif event.event_type == EVENT_LOCK_UNLOCKED:
|
||||||
self._state = STATE_UNLOCKED
|
self._state = STATE_UNLOCKED
|
||||||
elif data[ATTR_LAST_EVENT_TYPE] == EVENT_LOCK_ERROR:
|
elif event.event_type == EVENT_LOCK_ERROR:
|
||||||
self._state = STATE_UNKNOWN
|
self._state = STATE_UNKNOWN
|
||||||
else:
|
else:
|
||||||
self._state = None
|
self._state = None
|
||||||
|
|
||||||
self._attrs.update(data)
|
|
||||||
|
Reference in New Issue
Block a user