Add support for real-time data from SimpliSafe

This commit is contained in:
Aaron Bach
2020-02-02 17:24:38 -07:00
parent db6449c3fb
commit cf74f34d7e
5 changed files with 337 additions and 123 deletions

View File

@@ -3,8 +3,9 @@ import asyncio
import logging
from simplipy import API
from simplipy.errors import InvalidCredentialsError, SimplipyError
from simplipy.system.v3 import VOLUME_HIGH, VOLUME_LOW, VOLUME_MEDIUM, VOLUME_OFF
from simplipy.entity import EntityTypes
from simplipy.errors import InvalidCredentialsError, SimplipyError, WebsocketError
from simplipy.websocket import get_event_type_from_payload
import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT
@@ -21,36 +22,49 @@ from homeassistant.helpers.dispatcher import (
async_dispatcher_send,
)
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.event import async_call_later, async_track_time_interval
from homeassistant.helpers.service import (
async_register_admin_service,
verify_domain_control,
)
from homeassistant.util.dt import utc_from_timestamp
from .config_flow import configured_instances
from .const import DATA_CLIENT, DEFAULT_SCAN_INTERVAL, DOMAIN, TOPIC_UPDATE
from .const import (
ATTR_ALARM_DURATION,
ATTR_ALARM_VOLUME,
ATTR_CHIME_VOLUME,
ATTR_ENTRY_DELAY_AWAY,
ATTR_ENTRY_DELAY_HOME,
ATTR_EXIT_DELAY_AWAY,
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_VOICE_PROMPT_VOLUME,
DATA_CLIENT,
DEFAULT_SCAN_INTERVAL,
DOMAIN,
VOLUMES,
)
_LOGGER = logging.getLogger(__name__)
CONF_ACCOUNTS = "accounts"
DATA_LISTENER = "listener"
TOPIC_UPDATE = "update_{0}"
DEFAULT_SOCKET_MIN_RETRY = 15
DEFAULT_WATCHDOG_SECONDS = 5 * 60
ATTR_ALARM_DURATION = "alarm_duration"
ATTR_ALARM_VOLUME = "alarm_volume"
ATTR_CHIME_VOLUME = "chime_volume"
ATTR_ENTRY_DELAY_AWAY = "entry_delay_away"
ATTR_ENTRY_DELAY_HOME = "entry_delay_home"
ATTR_EXIT_DELAY_AWAY = "exit_delay_away"
ATTR_EXIT_DELAY_HOME = "exit_delay_home"
ATTR_LIGHT = "light"
ATTR_PIN_LABEL = "label"
ATTR_PIN_LABEL_OR_VALUE = "label_or_pin"
ATTR_PIN_VALUE = "pin"
ATTR_SYSTEM_ID = "system_id"
ATTR_VOICE_PROMPT_VOLUME = "voice_prompt_volume"
VOLUMES = [VOLUME_OFF, VOLUME_LOW, VOLUME_MEDIUM, VOLUME_HIGH]
SERVICE_BASE_SCHEMA = vol.Schema({vol.Required(ATTR_SYSTEM_ID): cv.positive_int})
@@ -117,6 +131,37 @@ 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):
"""Register a new bridge."""
device_registry = await dr.async_get_registry(hass)
@@ -292,9 +337,27 @@ class SimpliSafe:
self._config_entry = config_entry
self._emergency_refresh_token_used = False
self._hass = hass
self.last_event_data = {}
self._websocket_reconnect_delay = DEFAULT_SOCKET_MIN_RETRY
self._websocket_watchdog_listener = None
self.last_rest_api_data = {}
self.last_websocket_data = {}
self.systems = None
hass.loop.create_task(self.async_websocket_connect())
async def _attempt_websocket_connect(self):
"""Attempt to connect to the websocket (retrying later on fail)."""
try:
await self._api.websocket.async_connect()
except WebsocketError as err:
_LOGGER.error("Error with the websocket connection: %s", err)
self._websocket_reconnect_delay = min(
2 * self._websocket_reconnect_delay, 480
)
async_call_later(
self._hass, self._websocket_reconnect_delay, self.ws_connect
)
async def async_init(self):
"""Initialize the data class."""
self.systems = await self._api.get_systems()
@@ -323,7 +386,8 @@ class SimpliSafe:
async def update_system(system):
"""Update a system."""
await system.update()
self.last_event_data[system.system_id] = await system.get_latest_event()
_LOGGER.debug(f"Updated REST API data for system {system.system_id}")
async_dispatcher_send(self._hass, TOPIC_UPDATE.format(system.system_id))
tasks = [update_system(system) for system in self.systems.values()]
@@ -371,8 +435,52 @@ class SimpliSafe:
if self._emergency_refresh_token_used:
self._emergency_refresh_token_used = False
_LOGGER.debug("Updated data for all SimpliSafe systems")
async_dispatcher_send(self._hass, TOPIC_UPDATE)
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
_LOGGER.debug(f'Updated websocket data for system {data["sid"]}')
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):
@@ -382,8 +490,11 @@ class SimpliSafeEntity(Entity):
"""Initialize."""
self._async_unsub_dispatcher_connect = None
self._attrs = {ATTR_SYSTEM_ID: system.system_id}
self._last_used_rest_api_data = None
self._last_used_websocket_data = None
self._name = name
self._online = True
self._state = None
self._system = system
if serial:
@@ -436,9 +547,36 @@ class SimpliSafeEntity(Entity):
self.async_schedule_update_ha_state(True)
self._async_unsub_dispatcher_connect = async_dispatcher_connect(
self.hass, TOPIC_UPDATE, update
self.hass, TOPIC_UPDATE.format(self._system.system_id), update
)
async def async_update(self):
"""Update the entity."""
rest_data = self._simplisafe.last_rest_api_data.get(self._system.system_id)
ws_data = self._simplisafe.last_websocket_data.get(self._system.system_id)
# If the most recent REST API data (within the data object) doesn't match what
# this entity last used, update:
if self._last_used_rest_api_data != rest_data:
self._last_used_rest_api_data = rest_data
self.async_update_from_rest_api(rest_data)
# If the most recent websocket data (within the data object) doesn't match what
# this entity last used, update:
if self._last_used_websocket_data != ws_data:
self._last_used_websocket_data = ws_data
self.async_update_from_websocket_api(ws_data)
@callback
def async_update_from_rest_api(data):
"""Update the entity with the provided REST API data."""
raise NotImplementedError()
@callback
def async_update_from_websocket_api(data):
"""Update the entity with the provided websocket API data."""
pass
async def async_will_remove_from_hass(self) -> None:
"""Disconnect dispatcher listener when removed."""
if self._async_unsub_dispatcher_connect:

View File

@@ -2,9 +2,21 @@
import logging
import re
from simplipy.entity import EntityTypes
from simplipy.errors import SimplipyError
from simplipy.system import SystemStates
from simplipy.system.v3 import VOLUME_HIGH, VOLUME_LOW, VOLUME_MEDIUM, VOLUME_OFF
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 (
FORMAT_NUMBER,
@@ -23,40 +35,34 @@ from homeassistant.const import (
STATE_ALARM_DISARMED,
STATE_ALARM_TRIGGERED,
)
from homeassistant.util.dt import utc_from_timestamp
from homeassistant.core import callback
from . import SimpliSafeEntity
from .const import DATA_CLIENT, DOMAIN
from .const import (
ATTR_ALARM_DURATION,
ATTR_ALARM_VOLUME,
ATTR_CHIME_VOLUME,
ATTR_ENTRY_DELAY_AWAY,
ATTR_ENTRY_DELAY_HOME,
ATTR_EXIT_DELAY_AWAY,
ATTR_EXIT_DELAY_HOME,
ATTR_LAST_EVENT_TYPE,
ATTR_LIGHT,
ATTR_VOICE_PROMPT_VOLUME,
DATA_CLIENT,
DOMAIN,
VOLUME_STRING_MAP,
)
_LOGGER = logging.getLogger(__name__)
ATTR_ALARM_DURATION = "alarm_duration"
ATTR_ALARM_VOLUME = "alarm_volume"
ATTR_BATTERY_BACKUP_POWER_LEVEL = "battery_backup_power_level"
ATTR_CHIME_VOLUME = "chime_volume"
ATTR_ENTRY_DELAY_AWAY = "entry_delay_away"
ATTR_ENTRY_DELAY_HOME = "entry_delay_home"
ATTR_EXIT_DELAY_AWAY = "exit_delay_away"
ATTR_EXIT_DELAY_HOME = "exit_delay_home"
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_LIGHT = "light"
ATTR_PIN_NAME = "pin_name"
ATTR_RF_JAMMING = "rf_jamming"
ATTR_VOICE_PROMPT_VOLUME = "voice_prompt_volume"
ATTR_WALL_POWER_LEVEL = "wall_power_level"
ATTR_WIFI_STRENGTH = "wifi_strength"
VOLUME_STRING_MAP = {
VOLUME_HIGH: "high",
VOLUME_LOW: "low",
VOLUME_MEDIUM: "medium",
VOLUME_OFF: "off",
}
async def async_setup_entry(hass, entry, async_add_entities):
"""Set up a SimpliSafe alarm control panel based on a config entry."""
@@ -78,30 +84,25 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanel):
super().__init__(system, "Alarm Control Panel")
self._changed_by = None
self._code = code
self._last_event = None
self._simplisafe = simplisafe
self._state = None
if self._system.version == 3:
self._attrs.update(
{
ATTR_ALARM_DURATION: self._system.alarm_duration,
ATTR_ALARM_VOLUME: VOLUME_STRING_MAP[self._system.alarm_volume],
ATTR_BATTERY_BACKUP_POWER_LEVEL: self._system.battery_backup_power_level,
ATTR_CHIME_VOLUME: VOLUME_STRING_MAP[self._system.chime_volume],
ATTR_ENTRY_DELAY_AWAY: self._system.entry_delay_away,
ATTR_ENTRY_DELAY_HOME: self._system.entry_delay_home,
ATTR_EXIT_DELAY_AWAY: self._system.exit_delay_away,
ATTR_EXIT_DELAY_HOME: self._system.exit_delay_home,
ATTR_GSM_STRENGTH: self._system.gsm_strength,
ATTR_LIGHT: self._system.light,
ATTR_RF_JAMMING: self._system.rf_jamming,
ATTR_VOICE_PROMPT_VOLUME: VOLUME_STRING_MAP[
self._system.voice_prompt_volume
],
ATTR_WALL_POWER_LEVEL: self._system.wall_power_level,
ATTR_WIFI_STRENGTH: self._system.wifi_strength,
}
)
if system.alarm_going_off:
self._state = STATE_ALARM_TRIGGERED
elif system.state == SystemStates.away:
self._state = STATE_ALARM_ARMED_AWAY
elif system.state in (
SystemStates.away_count,
SystemStates.exit_delay,
SystemStates.home_count,
):
self._state = STATE_ALARM_ARMING
elif system.state == SystemStates.home:
self._state = STATE_ALARM_ARMED_HOME
elif system.state == SystemStates.off:
self._state = STATE_ALARM_DISARMED
else:
self._state = None
@property
def changed_by(self):
@@ -139,71 +140,100 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanel):
if not self._validate_code(code, "disarming"):
return
await self._system.set_off()
try:
await self._system.set_off()
except SimplipyError as err:
_LOGGER.error('Error while disarming "%s": %s', self._system.name, err)
return
self._state = STATE_ALARM_DISARMED
async def async_alarm_arm_home(self, code=None):
"""Send arm home command."""
if not self._validate_code(code, "arming home"):
return
await self._system.set_home()
try:
await self._system.set_home()
except SimplipyError as err:
_LOGGER.error('Error while arming "%s" (home): %s', self._system.name, err)
return
self._state = STATE_ALARM_ARMED_HOME
async def async_alarm_arm_away(self, code=None):
"""Send arm away command."""
if not self._validate_code(code, "arming away"):
return
await self._system.set_away()
try:
await self._system.set_away()
except SimplipyError as err:
_LOGGER.error('Error while arming "%s" (away): %s', self._system.name, err)
return
async def async_update(self):
"""Update alarm status."""
last_event = self._simplisafe.last_event_data[self._system.system_id]
if last_event.get("pinName"):
self._changed_by = last_event["pinName"]
self._state = STATE_ALARM_ARMING
@callback
def async_update_from_rest_api(self, data):
"""Update the entity with the provided REST API data."""
if self._system.state == SystemStates.error:
self._online = False
return
self._online = True
if self._system.alarm_going_off:
if self._system.version == 3:
self._attrs.update(
{
ATTR_ALARM_DURATION: self._system.alarm_duration,
ATTR_ALARM_VOLUME: VOLUME_STRING_MAP[self._system.alarm_volume],
ATTR_BATTERY_BACKUP_POWER_LEVEL: self._system.battery_backup_power_level,
ATTR_CHIME_VOLUME: VOLUME_STRING_MAP[self._system.chime_volume],
ATTR_ENTRY_DELAY_AWAY: self._system.entry_delay_away,
ATTR_ENTRY_DELAY_HOME: self._system.entry_delay_home,
ATTR_EXIT_DELAY_AWAY: self._system.exit_delay_away,
ATTR_EXIT_DELAY_HOME: self._system.exit_delay_home,
ATTR_GSM_STRENGTH: self._system.gsm_strength,
ATTR_LIGHT: self._system.light,
ATTR_RF_JAMMING: self._system.rf_jamming,
ATTR_VOICE_PROMPT_VOLUME: VOLUME_STRING_MAP[
self._system.voice_prompt_volume
],
ATTR_WALL_POWER_LEVEL: self._system.wall_power_level,
ATTR_WIFI_STRENGTH: self._system.wifi_strength,
}
)
@callback
def async_update_from_websocket_api(self, data):
"""Update the entity with the provided websocket API data."""
if data.get(ATTR_PIN_NAME):
self._changed_by = data[ATTR_PIN_NAME]
if data[ATTR_LAST_EVENT_TYPE] in (
EVENT_ALARM_CANCELED,
EVENT_DISARMED_BY_MASTER_PIN,
EVENT_DISARMED_BY_REMOTE,
):
self._state = STATE_ALARM_DISARMED
elif data[ATTR_LAST_EVENT_TYPE] == EVENT_ALARM_TRIGGERED:
self._state = STATE_ALARM_TRIGGERED
elif self._system.state == SystemStates.away:
elif data[ATTR_LAST_EVENT_TYPE] in (
EVENT_ARMED_AWAY,
EVENT_ARMED_AWAY_BY_KEYPAD,
EVENT_ARMED_AWAY_BY_REMOTE,
):
self._state = STATE_ALARM_ARMED_AWAY
elif self._system.state in (
SystemStates.away_count,
SystemStates.exit_delay,
SystemStates.home_count,
elif data[ATTR_LAST_EVENT_TYPE] == EVENT_ARMED_HOME:
self._state = STATE_ALARM_ARMED_HOME
elif data[ATTR_LAST_EVENT_TYPE] in (
EVENT_AWAY_EXIT_DELAY_BY_KEYPAD,
EVENT_AWAY_EXIT_DELAY_BY_REMOTE,
EVENT_HOME_EXIT_DELAY,
):
self._state = STATE_ALARM_ARMING
elif self._system.state == SystemStates.home:
self._state = STATE_ALARM_ARMED_HOME
elif self._system.state == SystemStates.off:
self._state = STATE_ALARM_DISARMED
else:
self._state = None
try:
last_event_sensor_type = EntityTypes(last_event["sensorType"]).name
except ValueError:
_LOGGER.warning(
'Encountered unknown entity type: %s ("%s"). Please report it at'
"https://github.com/home-assistant/home-assistant/issues.",
last_event["sensorType"],
last_event["sensorName"],
)
last_event_sensor_type = None
self._attrs.update(
{
ATTR_LAST_EVENT_INFO: last_event["info"],
ATTR_LAST_EVENT_SENSOR_NAME: last_event["sensorName"],
ATTR_LAST_EVENT_SENSOR_TYPE: last_event_sensor_type,
ATTR_LAST_EVENT_TIMESTAMP: utc_from_timestamp(
last_event["eventTimestamp"]
),
ATTR_LAST_EVENT_TYPE: last_event["eventType"],
}
)
self._attrs.update(data)

View File

@@ -1,10 +1,33 @@
"""Define constants for the SimpliSafe component."""
from datetime import timedelta
from simplipy.system.v3 import VOLUME_HIGH, VOLUME_LOW, VOLUME_MEDIUM, VOLUME_OFF
DOMAIN = "simplisafe"
DATA_CLIENT = "client"
DEFAULT_SCAN_INTERVAL = timedelta(seconds=30)
TOPIC_UPDATE = "update"
ATTR_ALARM_DURATION = "alarm_duration"
ATTR_ALARM_VOLUME = "alarm_volume"
ATTR_CHIME_VOLUME = "chime_volume"
ATTR_ENTRY_DELAY_AWAY = "entry_delay_away"
ATTR_ENTRY_DELAY_HOME = "entry_delay_home"
ATTR_EXIT_DELAY_AWAY = "exit_delay_away"
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_VOICE_PROMPT_VOLUME = "voice_prompt_volume"
VOLUMES = [VOLUME_OFF, VOLUME_LOW, VOLUME_MEDIUM, VOLUME_HIGH]
VOLUME_STRING_MAP = {
VOLUME_HIGH: "high",
VOLUME_LOW: "low",
VOLUME_MEDIUM: "medium",
VOLUME_OFF: "off",
}

View File

@@ -1,13 +1,16 @@
"""Support for SimpliSafe locks."""
import logging
from simplipy.errors import SimplipyError
from simplipy.lock import LockStates
from simplipy.websocket import EVENT_LOCK_ERROR, EVENT_LOCK_LOCKED, EVENT_LOCK_UNLOCKED
from homeassistant.components.lock import LockDevice
from homeassistant.const import STATE_LOCKED, STATE_UNKNOWN, STATE_UNLOCKED
from homeassistant.core import callback
from . import SimpliSafeEntity
from .const import DATA_CLIENT, DOMAIN
from .const import ATTR_LAST_EVENT_TYPE, DATA_CLIENT, DOMAIN
_LOGGER = logging.getLogger(__name__)
@@ -15,12 +18,6 @@ ATTR_LOCK_LOW_BATTERY = "lock_low_battery"
ATTR_JAMMED = "jammed"
ATTR_PIN_PAD_LOW_BATTERY = "pin_pad_low_battery"
STATE_MAP = {
LockStates.locked: STATE_LOCKED,
LockStates.unknown: STATE_UNKNOWN,
LockStates.unlocked: STATE_UNLOCKED,
}
async def async_setup_entry(hass, entry, async_add_entities):
"""Set up SimpliSafe locks based on a config entry."""
@@ -45,24 +42,36 @@ class SimpliSafeLock(SimpliSafeEntity, LockDevice):
@property
def is_locked(self):
"""Return true if the lock is locked."""
return STATE_MAP.get(self._lock.state) == STATE_LOCKED
return self._state == STATE_LOCKED
async def async_lock(self, **kwargs):
"""Lock the lock."""
await self._lock.lock()
try:
await self._lock.lock()
except SimplipyError as err:
_LOGGER.error('Error while locking "%s": %s', self._lock.name, err)
return
self._state = STATE_LOCKED
async def async_unlock(self, **kwargs):
"""Unlock the lock."""
await self._lock.unlock()
try:
await self._lock.unlock()
except SimplipyError as err:
_LOGGER.error('Error while unlocking "%s": %s', self._lock.name, err)
return
async def async_update(self):
"""Update lock status."""
self._state = STATE_UNLOCKED
@callback
def async_update_from_rest_api(self, data):
"""Update the entity with the provided REST API data."""
if self._lock.offline or self._lock.disabled:
self._online = False
return
self._online = True
self._attrs.update(
{
ATTR_LOCK_LOW_BATTERY: self._lock.lock_low_battery,
@@ -70,3 +79,17 @@ class SimpliSafeLock(SimpliSafeEntity, LockDevice):
ATTR_PIN_PAD_LOW_BATTERY: self._lock.pin_pad_low_battery,
}
)
@callback
def async_update_from_websocket_api(self, data):
"""Update the entity with the provided websocket API data."""
if data[ATTR_LAST_EVENT_TYPE] == EVENT_LOCK_LOCKED:
self._state = STATE_LOCKED
elif data[ATTR_LAST_EVENT_TYPE] == EVENT_LOCK_UNLOCKED:
self._state = STATE_UNLOCKED
elif data[ATTR_LAST_EVENT_TYPE] == EVENT_LOCK_ERROR:
self._state = STATE_UNKNOWN
else:
self._state = None
self._attrs.update(data)

View File

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