2019-02-13 20:21:14 +00:00
|
|
|
"""Support for August lock."""
|
2019-03-21 05:56:46 +00:00
|
|
|
import logging
|
2018-02-18 08:24:51 +00:00
|
|
|
|
2020-02-21 05:06:24 +00:00
|
|
|
from august.activity import ActivityType
|
2019-10-18 00:06:41 +00:00
|
|
|
from august.lock import LockStatus
|
2020-02-21 05:06:24 +00:00
|
|
|
from august.util import update_lock_detail_from_activity
|
2019-10-18 00:06:41 +00:00
|
|
|
|
2018-02-18 08:24:51 +00:00
|
|
|
from homeassistant.components.lock import LockDevice
|
|
|
|
from homeassistant.const import ATTR_BATTERY_LEVEL
|
2020-02-27 02:48:44 +00:00
|
|
|
from homeassistant.core import callback
|
|
|
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
2018-02-18 08:24:51 +00:00
|
|
|
|
2020-02-27 02:48:44 +00:00
|
|
|
from .const import (
|
|
|
|
AUGUST_DEVICE_UPDATE,
|
|
|
|
DATA_AUGUST,
|
|
|
|
DEFAULT_NAME,
|
|
|
|
DOMAIN,
|
|
|
|
MIN_TIME_BETWEEN_DETAIL_UPDATES,
|
|
|
|
)
|
2019-03-21 05:56:46 +00:00
|
|
|
|
2018-10-19 07:37:02 +00:00
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
2020-02-27 02:48:44 +00:00
|
|
|
SCAN_INTERVAL = MIN_TIME_BETWEEN_DETAIL_UPDATES
|
2018-02-18 08:24:51 +00:00
|
|
|
|
|
|
|
|
2020-02-25 18:18:15 +00:00
|
|
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
2018-02-18 08:24:51 +00:00
|
|
|
"""Set up August locks."""
|
2020-02-25 18:18:15 +00:00
|
|
|
data = hass.data[DOMAIN][config_entry.entry_id][DATA_AUGUST]
|
2018-02-18 08:24:51 +00:00
|
|
|
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))
|
|
|
|
|
2020-02-16 05:08:52 +00:00
|
|
|
async_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."""
|
2020-02-27 02:48:44 +00:00
|
|
|
self._undo_dispatch_subscription = None
|
2018-02-18 08:24:51 +00:00
|
|
|
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
|
2020-02-25 18:18:15 +00:00
|
|
|
self._firmware_version = None
|
2020-02-26 02:34:31 +00:00
|
|
|
self._model = None
|
2018-02-18 08:24:51 +00:00
|
|
|
|
2020-02-16 05:08:52 +00:00
|
|
|
async def async_lock(self, **kwargs):
|
2018-02-18 08:24:51 +00:00
|
|
|
"""Lock the device."""
|
2020-02-23 21:54:35 +00:00
|
|
|
await self._call_lock_operation(self._data.lock)
|
2018-02-18 08:24:51 +00:00
|
|
|
|
2020-02-16 05:08:52 +00:00
|
|
|
async def async_unlock(self, **kwargs):
|
2018-02-18 08:24:51 +00:00
|
|
|
"""Unlock the device."""
|
2020-02-23 21:54:35 +00:00
|
|
|
await self._call_lock_operation(self._data.unlock)
|
|
|
|
|
|
|
|
async def _call_lock_operation(self, lock_operation):
|
|
|
|
activities = await self.hass.async_add_executor_job(
|
|
|
|
lock_operation, self._lock.device_id
|
2020-02-16 05:08:52 +00:00
|
|
|
)
|
2020-02-23 21:54:35 +00:00
|
|
|
for lock_activity in activities:
|
|
|
|
update_lock_detail_from_activity(self._lock_detail, lock_activity)
|
2020-02-11 16:57:26 +00:00
|
|
|
|
2020-02-23 21:54:35 +00:00
|
|
|
if self._update_lock_status_from_detail():
|
2020-02-27 02:48:44 +00:00
|
|
|
await self._data.async_signal_operation_changed_device_state(
|
|
|
|
self._lock.device_id
|
|
|
|
)
|
2020-02-23 21:54:35 +00:00
|
|
|
|
|
|
|
def _update_lock_status_from_detail(self):
|
2020-02-25 18:18:15 +00:00
|
|
|
detail = self._lock_detail
|
|
|
|
lock_status = None
|
|
|
|
self._available = False
|
|
|
|
|
|
|
|
if detail is not None:
|
|
|
|
lock_status = detail.lock_status
|
2020-02-26 07:43:41 +00:00
|
|
|
self._available = detail.bridge_is_online
|
2020-02-25 18:18:15 +00:00
|
|
|
|
|
|
|
if self._lock_status != lock_status:
|
|
|
|
self._lock_status = lock_status
|
2020-02-23 21:54:35 +00:00
|
|
|
return True
|
|
|
|
return False
|
2018-02-18 08:24:51 +00:00
|
|
|
|
2020-02-16 05:08:52 +00:00
|
|
|
async def async_update(self):
|
|
|
|
"""Get the latest state of the sensor and update activity."""
|
|
|
|
self._lock_detail = await self._data.async_get_lock_detail(self._lock.device_id)
|
2020-02-27 02:48:44 +00:00
|
|
|
lock_activity = self._data.activity_stream.async_get_latest_device_activity(
|
|
|
|
self._lock.device_id, [ActivityType.LOCK_OPERATION]
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
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
|
2020-02-25 18:18:15 +00:00
|
|
|
if self._lock_detail is not None:
|
|
|
|
update_lock_detail_from_activity(self._lock_detail, lock_activity)
|
|
|
|
|
|
|
|
if self._lock_detail is not None:
|
|
|
|
self._firmware_version = self._lock_detail.firmware_version
|
2020-02-26 02:34:31 +00:00
|
|
|
self._model = self._lock_detail.model
|
2020-02-11 16:57:26 +00:00
|
|
|
|
2020-02-23 21:54:35 +00:00
|
|
|
self._update_lock_status_from_detail()
|
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."""
|
2020-02-25 18:18:15 +00:00
|
|
|
if self._lock_status is None or self._lock_status is LockStatus.UNKNOWN:
|
|
|
|
return None
|
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
|
|
|
|
2020-02-25 18:18:15 +00:00
|
|
|
@property
|
|
|
|
def device_info(self):
|
|
|
|
"""Return the device_info of the device."""
|
|
|
|
return {
|
|
|
|
"identifiers": {(DOMAIN, self._lock.device_id)},
|
|
|
|
"name": self._lock.device_name,
|
|
|
|
"manufacturer": DEFAULT_NAME,
|
|
|
|
"sw_version": self._firmware_version,
|
2020-02-26 02:34:31 +00:00
|
|
|
"model": self._model,
|
2020-02-25 18:18:15 +00:00
|
|
|
}
|
|
|
|
|
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"
|
2020-02-27 02:48:44 +00:00
|
|
|
|
|
|
|
async def async_added_to_hass(self):
|
|
|
|
"""Register callbacks."""
|
|
|
|
|
|
|
|
@callback
|
|
|
|
def update():
|
|
|
|
"""Update the state."""
|
|
|
|
self.async_schedule_update_ha_state(True)
|
|
|
|
|
|
|
|
self._undo_dispatch_subscription = async_dispatcher_connect(
|
|
|
|
self.hass, f"{AUGUST_DEVICE_UPDATE}-{self._lock.device_id}", update
|
|
|
|
)
|
|
|
|
|
|
|
|
async def async_will_remove_from_hass(self):
|
|
|
|
"""Undo subscription."""
|
|
|
|
if self._undo_dispatch_subscription:
|
|
|
|
self._undo_dispatch_subscription()
|