2019-02-13 20:21:14 +00:00
|
|
|
"""Support for August lock."""
|
2018-02-18 08:24:51 +00:00
|
|
|
from datetime import timedelta
|
2019-03-21 05:56:46 +00:00
|
|
|
import logging
|
2018-02-18 08:24:51 +00:00
|
|
|
|
2020-02-12 23:35:07 +00:00
|
|
|
from august.activity import ACTIVITY_ACTION_STATES, ActivityType
|
2019-10-18 00:06:41 +00:00
|
|
|
from august.lock import LockStatus
|
|
|
|
|
2018-02-18 08:24:51 +00:00
|
|
|
from homeassistant.components.lock import LockDevice
|
|
|
|
from homeassistant.const import ATTR_BATTERY_LEVEL
|
2020-02-11 16:57:26 +00:00
|
|
|
from homeassistant.util import dt
|
2018-02-18 08:24:51 +00:00
|
|
|
|
2019-03-21 05:56:46 +00:00
|
|
|
from . import DATA_AUGUST
|
|
|
|
|
2018-10-19 07:37:02 +00:00
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
2020-02-08 19:22:48 +00:00
|
|
|
SCAN_INTERVAL = timedelta(seconds=10)
|
2018-02-18 08:24:51 +00:00
|
|
|
|
|
|
|
|
2018-08-24 14:37:30 +00:00
|
|
|
def setup_platform(hass, config, add_entities, discovery_info=None):
|
2018-02-18 08:24:51 +00:00
|
|
|
"""Set up August locks."""
|
|
|
|
data = hass.data[DATA_AUGUST]
|
|
|
|
devices = []
|
|
|
|
|
|
|
|
for lock in data.locks:
|
2018-10-19 07:37:02 +00:00
|
|
|
_LOGGER.debug("Adding lock for %s", lock.device_name)
|
2018-02-18 08:24:51 +00:00
|
|
|
devices.append(AugustLock(data, lock))
|
|
|
|
|
2018-08-24 14:37:30 +00:00
|
|
|
add_entities(devices, True)
|
2018-02-18 08:24:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
class AugustLock(LockDevice):
|
|
|
|
"""Representation of an August lock."""
|
|
|
|
|
|
|
|
def __init__(self, data, lock):
|
|
|
|
"""Initialize the lock."""
|
|
|
|
self._data = data
|
|
|
|
self._lock = lock
|
|
|
|
self._lock_status = None
|
|
|
|
self._lock_detail = None
|
|
|
|
self._changed_by = None
|
2018-10-23 12:09:08 +00:00
|
|
|
self._available = False
|
2018-02-18 08:24:51 +00:00
|
|
|
|
|
|
|
def lock(self, **kwargs):
|
|
|
|
"""Lock the device."""
|
2020-02-11 16:57:26 +00:00
|
|
|
update_start_time_utc = dt.utcnow()
|
|
|
|
lock_status = self._data.lock(self._lock.device_id)
|
|
|
|
self._update_lock_status(lock_status, update_start_time_utc)
|
2018-02-18 08:24:51 +00:00
|
|
|
|
|
|
|
def unlock(self, **kwargs):
|
|
|
|
"""Unlock the device."""
|
2020-02-11 16:57:26 +00:00
|
|
|
update_start_time_utc = dt.utcnow()
|
|
|
|
lock_status = self._data.unlock(self._lock.device_id)
|
|
|
|
self._update_lock_status(lock_status, update_start_time_utc)
|
|
|
|
|
|
|
|
def _update_lock_status(self, lock_status, update_start_time_utc):
|
|
|
|
if self._lock_status != lock_status:
|
|
|
|
self._lock_status = lock_status
|
|
|
|
self._data.update_lock_status(
|
|
|
|
self._lock.device_id, lock_status, update_start_time_utc
|
|
|
|
)
|
|
|
|
self.schedule_update_ha_state()
|
2018-02-18 08:24:51 +00:00
|
|
|
|
|
|
|
def update(self):
|
|
|
|
"""Get the latest state of the sensor."""
|
|
|
|
self._lock_status = self._data.get_lock_status(self._lock.device_id)
|
2018-10-23 12:09:08 +00:00
|
|
|
self._available = self._lock_status is not None
|
|
|
|
|
2018-02-18 08:24:51 +00:00
|
|
|
self._lock_detail = self._data.get_lock_detail(self._lock.device_id)
|
|
|
|
|
2020-02-12 23:35:07 +00:00
|
|
|
lock_activity = self._data.get_latest_device_activity(
|
2019-07-31 19:25:30 +00:00
|
|
|
self._lock.device_id, ActivityType.LOCK_OPERATION
|
|
|
|
)
|
2018-02-18 08:24:51 +00:00
|
|
|
|
2020-02-12 23:35:07 +00:00
|
|
|
if lock_activity is not None:
|
|
|
|
self._changed_by = lock_activity.operated_by
|
|
|
|
self._sync_lock_activity(lock_activity)
|
2020-02-11 16:57:26 +00:00
|
|
|
|
2020-02-12 23:35:07 +00:00
|
|
|
def _sync_lock_activity(self, lock_activity):
|
2020-02-11 16:57:26 +00:00
|
|
|
"""Check the activity for the latest lock/unlock activity (events).
|
|
|
|
|
|
|
|
We use this to determine the lock state in between calls to the lock
|
|
|
|
api as we update it more frequently
|
|
|
|
"""
|
|
|
|
last_lock_status_update_time_utc = self._data.get_last_lock_status_update_time_utc(
|
|
|
|
self._lock.device_id
|
|
|
|
)
|
2020-02-12 23:35:07 +00:00
|
|
|
activity_end_time_utc = dt.as_utc(lock_activity.activity_end_time)
|
2020-02-11 16:57:26 +00:00
|
|
|
|
|
|
|
if activity_end_time_utc > last_lock_status_update_time_utc:
|
|
|
|
_LOGGER.debug(
|
|
|
|
"The activity log has new events for %s: [action=%s] [activity_end_time_utc=%s] > [last_lock_status_update_time_utc=%s]",
|
|
|
|
self.name,
|
2020-02-12 23:35:07 +00:00
|
|
|
lock_activity.action,
|
2020-02-11 16:57:26 +00:00
|
|
|
activity_end_time_utc,
|
|
|
|
last_lock_status_update_time_utc,
|
|
|
|
)
|
2020-02-12 23:35:07 +00:00
|
|
|
activity_start_time_utc = dt.as_utc(lock_activity.activity_start_time)
|
|
|
|
if lock_activity.action in ACTIVITY_ACTION_STATES:
|
|
|
|
self._update_lock_status(
|
|
|
|
ACTIVITY_ACTION_STATES[lock_activity.action],
|
|
|
|
activity_start_time_utc,
|
|
|
|
)
|
2020-02-11 16:57:26 +00:00
|
|
|
else:
|
|
|
|
_LOGGER.info(
|
|
|
|
"Unhandled lock activity action %s for %s",
|
2020-02-12 23:35:07 +00:00
|
|
|
lock_activity.action,
|
2020-02-11 16:57:26 +00:00
|
|
|
self.name,
|
|
|
|
)
|
2018-02-18 08:24:51 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def name(self):
|
|
|
|
"""Return the name of this device."""
|
|
|
|
return self._lock.device_name
|
|
|
|
|
2018-10-23 12:09:08 +00:00
|
|
|
@property
|
|
|
|
def available(self):
|
|
|
|
"""Return the availability of this sensor."""
|
|
|
|
return self._available
|
|
|
|
|
2018-02-18 08:24:51 +00:00
|
|
|
@property
|
|
|
|
def is_locked(self):
|
|
|
|
"""Return true if device is on."""
|
2019-07-31 19:25:30 +00:00
|
|
|
|
2018-02-18 08:24:51 +00:00
|
|
|
return self._lock_status is LockStatus.LOCKED
|
|
|
|
|
|
|
|
@property
|
|
|
|
def changed_by(self):
|
|
|
|
"""Last change triggered by."""
|
|
|
|
return self._changed_by
|
|
|
|
|
|
|
|
@property
|
|
|
|
def device_state_attributes(self):
|
|
|
|
"""Return the device specific state attributes."""
|
2018-10-19 07:37:02 +00:00
|
|
|
if self._lock_detail is None:
|
|
|
|
return None
|
|
|
|
|
2020-02-08 19:22:48 +00:00
|
|
|
attributes = {ATTR_BATTERY_LEVEL: self._lock_detail.battery_level}
|
|
|
|
|
|
|
|
if self._lock_detail.keypad is not None:
|
|
|
|
attributes["keypad_battery_level"] = self._lock_detail.keypad.battery_level
|
|
|
|
|
|
|
|
return attributes
|
2019-02-11 19:48:02 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def unique_id(self) -> str:
|
|
|
|
"""Get the unique id of the lock."""
|
2019-09-03 14:11:36 +00:00
|
|
|
return f"{self._lock.device_id:s}_lock"
|