mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 16:27:08 +00:00
Support for August doorbell (#11124)
* Add support for August doorbell * Address PR comment for August platform * Address PR comment for August binary sensor * Address PR comment for August camera * Addressed PR comment for August lock * - Fixed houndci-bot error * - Updated configurator description * - Fixed stale docstring * Added august module to .coveragerc
This commit is contained in:
parent
e8d8b75c07
commit
a8444b22e7
@ -38,6 +38,9 @@ omit =
|
|||||||
homeassistant/components/asterisk_mbox.py
|
homeassistant/components/asterisk_mbox.py
|
||||||
homeassistant/components/*/asterisk_mbox.py
|
homeassistant/components/*/asterisk_mbox.py
|
||||||
|
|
||||||
|
homeassistant/components/august.py
|
||||||
|
homeassistant/components/*/august.py
|
||||||
|
|
||||||
homeassistant/components/axis.py
|
homeassistant/components/axis.py
|
||||||
homeassistant/components/*/axis.py
|
homeassistant/components/*/axis.py
|
||||||
|
|
||||||
|
257
homeassistant/components/august.py
Normal file
257
homeassistant/components/august.py
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
"""
|
||||||
|
Support for August devices.
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/august/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
from requests import RequestException
|
||||||
|
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
from homeassistant.const import (
|
||||||
|
CONF_PASSWORD, CONF_USERNAME, CONF_TIMEOUT)
|
||||||
|
from homeassistant.helpers import discovery
|
||||||
|
from homeassistant.util import Throttle
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
_CONFIGURING = {}
|
||||||
|
|
||||||
|
REQUIREMENTS = ['py-august==0.3.0']
|
||||||
|
|
||||||
|
DEFAULT_TIMEOUT = 10
|
||||||
|
ACTIVITY_FETCH_LIMIT = 10
|
||||||
|
ACTIVITY_INITIAL_FETCH_LIMIT = 20
|
||||||
|
|
||||||
|
CONF_LOGIN_METHOD = 'login_method'
|
||||||
|
CONF_INSTALL_ID = 'install_id'
|
||||||
|
|
||||||
|
NOTIFICATION_ID = 'august_notification'
|
||||||
|
NOTIFICATION_TITLE = "August Setup"
|
||||||
|
|
||||||
|
AUGUST_CONFIG_FILE = '.august.conf'
|
||||||
|
|
||||||
|
DATA_AUGUST = 'august'
|
||||||
|
DOMAIN = 'august'
|
||||||
|
DEFAULT_ENTITY_NAMESPACE = 'august'
|
||||||
|
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=5)
|
||||||
|
DEFAULT_SCAN_INTERVAL = timedelta(seconds=5)
|
||||||
|
LOGIN_METHODS = ['phone', 'email']
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = vol.Schema({
|
||||||
|
DOMAIN: vol.Schema({
|
||||||
|
vol.Required(CONF_LOGIN_METHOD): vol.In(LOGIN_METHODS),
|
||||||
|
vol.Required(CONF_USERNAME): cv.string,
|
||||||
|
vol.Required(CONF_PASSWORD): cv.string,
|
||||||
|
vol.Optional(CONF_INSTALL_ID): cv.string,
|
||||||
|
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
|
||||||
|
})
|
||||||
|
}, extra=vol.ALLOW_EXTRA)
|
||||||
|
|
||||||
|
AUGUST_COMPONENTS = [
|
||||||
|
'camera', 'binary_sensor', 'lock'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def request_configuration(hass, config, api, authenticator):
|
||||||
|
"""Request configuration steps from the user."""
|
||||||
|
configurator = hass.components.configurator
|
||||||
|
|
||||||
|
def august_configuration_callback(data):
|
||||||
|
"""Run when the configuration callback is called."""
|
||||||
|
from august.authenticator import ValidationResult
|
||||||
|
|
||||||
|
result = authenticator.validate_verification_code(
|
||||||
|
data.get('verification_code'))
|
||||||
|
|
||||||
|
if result == ValidationResult.INVALID_VERIFICATION_CODE:
|
||||||
|
configurator.notify_errors(_CONFIGURING[DOMAIN],
|
||||||
|
"Invalid verification code")
|
||||||
|
elif result == ValidationResult.VALIDATED:
|
||||||
|
setup_august(hass, config, api, authenticator)
|
||||||
|
|
||||||
|
if DOMAIN not in _CONFIGURING:
|
||||||
|
authenticator.send_verification_code()
|
||||||
|
|
||||||
|
conf = config[DOMAIN]
|
||||||
|
username = conf.get(CONF_USERNAME)
|
||||||
|
login_method = conf.get(CONF_LOGIN_METHOD)
|
||||||
|
|
||||||
|
_CONFIGURING[DOMAIN] = configurator.request_config(
|
||||||
|
NOTIFICATION_TITLE,
|
||||||
|
august_configuration_callback,
|
||||||
|
description="Please check your {} ({}) and enter the verification "
|
||||||
|
"code below".format(login_method, username),
|
||||||
|
submit_caption='Verify',
|
||||||
|
fields=[{
|
||||||
|
'id': 'verification_code',
|
||||||
|
'name': "Verification code",
|
||||||
|
'type': 'string'}]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_august(hass, config, api, authenticator):
|
||||||
|
"""Set up the August component."""
|
||||||
|
from august.authenticator import AuthenticationState
|
||||||
|
|
||||||
|
authentication = None
|
||||||
|
try:
|
||||||
|
authentication = authenticator.authenticate()
|
||||||
|
except RequestException as ex:
|
||||||
|
_LOGGER.error("Unable to connect to August service: %s", str(ex))
|
||||||
|
|
||||||
|
hass.components.persistent_notification.create(
|
||||||
|
"Error: {}<br />"
|
||||||
|
"You will need to restart hass after fixing."
|
||||||
|
"".format(ex),
|
||||||
|
title=NOTIFICATION_TITLE,
|
||||||
|
notification_id=NOTIFICATION_ID)
|
||||||
|
|
||||||
|
state = authentication.state
|
||||||
|
|
||||||
|
if state == AuthenticationState.AUTHENTICATED:
|
||||||
|
if DOMAIN in _CONFIGURING:
|
||||||
|
hass.components.configurator.request_done(_CONFIGURING.pop(DOMAIN))
|
||||||
|
|
||||||
|
hass.data[DATA_AUGUST] = AugustData(api, authentication.access_token)
|
||||||
|
|
||||||
|
for component in AUGUST_COMPONENTS:
|
||||||
|
discovery.load_platform(hass, component, DOMAIN, {}, config)
|
||||||
|
|
||||||
|
return True
|
||||||
|
elif state == AuthenticationState.BAD_PASSWORD:
|
||||||
|
return False
|
||||||
|
elif state == AuthenticationState.REQUIRES_VALIDATION:
|
||||||
|
request_configuration(hass, config, api, authenticator)
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def setup(hass, config):
|
||||||
|
"""Set up the August component."""
|
||||||
|
from august.api import Api
|
||||||
|
from august.authenticator import Authenticator
|
||||||
|
|
||||||
|
conf = config[DOMAIN]
|
||||||
|
api = Api(timeout=conf.get(CONF_TIMEOUT))
|
||||||
|
|
||||||
|
authenticator = Authenticator(
|
||||||
|
api,
|
||||||
|
conf.get(CONF_LOGIN_METHOD),
|
||||||
|
conf.get(CONF_USERNAME),
|
||||||
|
conf.get(CONF_PASSWORD),
|
||||||
|
install_id=conf.get(CONF_INSTALL_ID),
|
||||||
|
access_token_cache_file=hass.config.path(AUGUST_CONFIG_FILE))
|
||||||
|
|
||||||
|
return setup_august(hass, config, api, authenticator)
|
||||||
|
|
||||||
|
|
||||||
|
class AugustData:
|
||||||
|
"""August data object."""
|
||||||
|
|
||||||
|
def __init__(self, api, access_token):
|
||||||
|
"""Init August data object."""
|
||||||
|
self._api = api
|
||||||
|
self._access_token = access_token
|
||||||
|
self._doorbells = self._api.get_doorbells(self._access_token) or []
|
||||||
|
self._locks = self._api.get_locks(self._access_token) or []
|
||||||
|
self._house_ids = [d.house_id for d in self._doorbells + self._locks]
|
||||||
|
|
||||||
|
self._doorbell_detail_by_id = {}
|
||||||
|
self._lock_status_by_id = {}
|
||||||
|
self._lock_detail_by_id = {}
|
||||||
|
self._activities_by_id = {}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def house_ids(self):
|
||||||
|
"""Return a list of house_ids."""
|
||||||
|
return self._house_ids
|
||||||
|
|
||||||
|
@property
|
||||||
|
def doorbells(self):
|
||||||
|
"""Return a list of doorbells."""
|
||||||
|
return self._doorbells
|
||||||
|
|
||||||
|
@property
|
||||||
|
def locks(self):
|
||||||
|
"""Return a list of locks."""
|
||||||
|
return self._locks
|
||||||
|
|
||||||
|
def get_device_activities(self, device_id, *activity_types):
|
||||||
|
"""Return a list of activities."""
|
||||||
|
self._update_device_activities()
|
||||||
|
|
||||||
|
activities = self._activities_by_id.get(device_id, [])
|
||||||
|
if activity_types:
|
||||||
|
return [a for a in activities if a.activity_type in activity_types]
|
||||||
|
return activities
|
||||||
|
|
||||||
|
def get_latest_device_activity(self, device_id, *activity_types):
|
||||||
|
"""Return latest activity."""
|
||||||
|
activities = self.get_device_activities(device_id, *activity_types)
|
||||||
|
return next(iter(activities or []), None)
|
||||||
|
|
||||||
|
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||||
|
def _update_device_activities(self, limit=ACTIVITY_FETCH_LIMIT):
|
||||||
|
"""Update data object with latest from August API."""
|
||||||
|
for house_id in self.house_ids:
|
||||||
|
activities = self._api.get_house_activities(self._access_token,
|
||||||
|
house_id,
|
||||||
|
limit=limit)
|
||||||
|
|
||||||
|
device_ids = {a.device_id for a in activities}
|
||||||
|
for device_id in device_ids:
|
||||||
|
self._activities_by_id[device_id] = [a for a in activities if
|
||||||
|
a.device_id == device_id]
|
||||||
|
|
||||||
|
def get_doorbell_detail(self, doorbell_id):
|
||||||
|
"""Return doorbell detail."""
|
||||||
|
self._update_doorbells()
|
||||||
|
return self._doorbell_detail_by_id.get(doorbell_id)
|
||||||
|
|
||||||
|
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||||
|
def _update_doorbells(self):
|
||||||
|
detail_by_id = {}
|
||||||
|
|
||||||
|
for doorbell in self._doorbells:
|
||||||
|
detail_by_id[doorbell.device_id] = self._api.get_doorbell_detail(
|
||||||
|
self._access_token, doorbell.device_id)
|
||||||
|
|
||||||
|
self._doorbell_detail_by_id = detail_by_id
|
||||||
|
|
||||||
|
def get_lock_status(self, lock_id):
|
||||||
|
"""Return lock status."""
|
||||||
|
self._update_locks()
|
||||||
|
return self._lock_status_by_id.get(lock_id)
|
||||||
|
|
||||||
|
def get_lock_detail(self, lock_id):
|
||||||
|
"""Return lock detail."""
|
||||||
|
self._update_locks()
|
||||||
|
return self._lock_detail_by_id.get(lock_id)
|
||||||
|
|
||||||
|
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||||
|
def _update_locks(self):
|
||||||
|
status_by_id = {}
|
||||||
|
detail_by_id = {}
|
||||||
|
|
||||||
|
for lock in self._locks:
|
||||||
|
status_by_id[lock.device_id] = self._api.get_lock_status(
|
||||||
|
self._access_token, lock.device_id)
|
||||||
|
detail_by_id[lock.device_id] = self._api.get_lock_detail(
|
||||||
|
self._access_token, lock.device_id)
|
||||||
|
|
||||||
|
self._lock_status_by_id = status_by_id
|
||||||
|
self._lock_detail_by_id = detail_by_id
|
||||||
|
|
||||||
|
def lock(self, device_id):
|
||||||
|
"""Lock the device."""
|
||||||
|
return self._api.lock(self._access_token, device_id)
|
||||||
|
|
||||||
|
def unlock(self, device_id):
|
||||||
|
"""Unlock the device."""
|
||||||
|
return self._api.unlock(self._access_token, device_id)
|
97
homeassistant/components/binary_sensor/august.py
Normal file
97
homeassistant/components/binary_sensor/august.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
"""
|
||||||
|
Support for August binary sensors.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/sensor.august/
|
||||||
|
"""
|
||||||
|
from datetime import timedelta, datetime
|
||||||
|
|
||||||
|
from homeassistant.components.august import DATA_AUGUST
|
||||||
|
from homeassistant.components.binary_sensor import (BinarySensorDevice)
|
||||||
|
|
||||||
|
DEPENDENCIES = ['august']
|
||||||
|
|
||||||
|
SCAN_INTERVAL = timedelta(seconds=5)
|
||||||
|
|
||||||
|
|
||||||
|
def _retrieve_online_state(data, doorbell):
|
||||||
|
"""Get the latest state of the sensor."""
|
||||||
|
detail = data.get_doorbell_detail(doorbell.device_id)
|
||||||
|
return detail.is_online
|
||||||
|
|
||||||
|
|
||||||
|
def _retrieve_motion_state(data, doorbell):
|
||||||
|
from august.activity import ActivityType
|
||||||
|
return _activity_time_based_state(data, doorbell,
|
||||||
|
[ActivityType.DOORBELL_MOTION,
|
||||||
|
ActivityType.DOORBELL_DING])
|
||||||
|
|
||||||
|
|
||||||
|
def _retrieve_ding_state(data, doorbell):
|
||||||
|
from august.activity import ActivityType
|
||||||
|
return _activity_time_based_state(data, doorbell,
|
||||||
|
[ActivityType.DOORBELL_DING])
|
||||||
|
|
||||||
|
|
||||||
|
def _activity_time_based_state(data, doorbell, activity_types):
|
||||||
|
"""Get the latest state of the sensor."""
|
||||||
|
latest = data.get_latest_device_activity(doorbell.device_id,
|
||||||
|
*activity_types)
|
||||||
|
|
||||||
|
if latest is not None:
|
||||||
|
start = latest.activity_start_time
|
||||||
|
end = latest.activity_end_time + timedelta(seconds=30)
|
||||||
|
return start <= datetime.now() <= end
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# Sensor types: Name, device_class, state_provider
|
||||||
|
SENSOR_TYPES = {
|
||||||
|
'doorbell_ding': ['Ding', 'occupancy', _retrieve_ding_state],
|
||||||
|
'doorbell_motion': ['Motion', 'motion', _retrieve_motion_state],
|
||||||
|
'doorbell_online': ['Online', 'connectivity', _retrieve_online_state],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
"""Set up the August binary sensors."""
|
||||||
|
data = hass.data[DATA_AUGUST]
|
||||||
|
devices = []
|
||||||
|
|
||||||
|
for doorbell in data.doorbells:
|
||||||
|
for sensor_type in SENSOR_TYPES:
|
||||||
|
devices.append(AugustBinarySensor(data, sensor_type, doorbell))
|
||||||
|
|
||||||
|
add_devices(devices, True)
|
||||||
|
|
||||||
|
|
||||||
|
class AugustBinarySensor(BinarySensorDevice):
|
||||||
|
"""Representation of an August binary sensor."""
|
||||||
|
|
||||||
|
def __init__(self, data, sensor_type, doorbell):
|
||||||
|
"""Initialize the sensor."""
|
||||||
|
self._data = data
|
||||||
|
self._sensor_type = sensor_type
|
||||||
|
self._doorbell = doorbell
|
||||||
|
self._state = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self):
|
||||||
|
"""Return true if the binary sensor is on."""
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_class(self):
|
||||||
|
"""Return the class of this device, from component DEVICE_CLASSES."""
|
||||||
|
return SENSOR_TYPES[self._sensor_type][1]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Return the name of the binary sensor."""
|
||||||
|
return "{} {}".format(self._doorbell.device_name,
|
||||||
|
SENSOR_TYPES[self._sensor_type][0])
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""Get the latest state of the sensor."""
|
||||||
|
state_provider = SENSOR_TYPES[self._sensor_type][2]
|
||||||
|
self._state = state_provider(self._data, self._doorbell)
|
76
homeassistant/components/camera/august.py
Normal file
76
homeassistant/components/camera/august.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
"""
|
||||||
|
Support for August camera.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/camera.august/
|
||||||
|
"""
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from homeassistant.components.august import DATA_AUGUST, DEFAULT_TIMEOUT
|
||||||
|
from homeassistant.components.camera import Camera
|
||||||
|
|
||||||
|
DEPENDENCIES = ['august']
|
||||||
|
|
||||||
|
SCAN_INTERVAL = timedelta(seconds=5)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
"""Set up August cameras."""
|
||||||
|
data = hass.data[DATA_AUGUST]
|
||||||
|
devices = []
|
||||||
|
|
||||||
|
for doorbell in data.doorbells:
|
||||||
|
devices.append(AugustCamera(data, doorbell, DEFAULT_TIMEOUT))
|
||||||
|
|
||||||
|
add_devices(devices, True)
|
||||||
|
|
||||||
|
|
||||||
|
class AugustCamera(Camera):
|
||||||
|
"""An implementation of a Canary security camera."""
|
||||||
|
|
||||||
|
def __init__(self, data, doorbell, timeout):
|
||||||
|
"""Initialize a Canary security camera."""
|
||||||
|
super().__init__()
|
||||||
|
self._data = data
|
||||||
|
self._doorbell = doorbell
|
||||||
|
self._timeout = timeout
|
||||||
|
self._image_url = None
|
||||||
|
self._image_content = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Return the name of this device."""
|
||||||
|
return self._doorbell.device_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_recording(self):
|
||||||
|
"""Return true if the device is recording."""
|
||||||
|
return self._doorbell.has_subscription
|
||||||
|
|
||||||
|
@property
|
||||||
|
def motion_detection_enabled(self):
|
||||||
|
"""Return the camera motion detection status."""
|
||||||
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def brand(self):
|
||||||
|
"""Return the camera brand."""
|
||||||
|
return 'August'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def model(self):
|
||||||
|
"""Return the camera model."""
|
||||||
|
return 'Doorbell'
|
||||||
|
|
||||||
|
def camera_image(self):
|
||||||
|
"""Return bytes of camera image."""
|
||||||
|
latest = self._data.get_doorbell_detail(self._doorbell.device_id)
|
||||||
|
|
||||||
|
if self._image_url is not latest.image_url:
|
||||||
|
self._image_url = latest.image_url
|
||||||
|
self._image_content = requests.get(self._image_url,
|
||||||
|
timeout=self._timeout).content
|
||||||
|
|
||||||
|
return self._image_content
|
82
homeassistant/components/lock/august.py
Normal file
82
homeassistant/components/lock/august.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
"""
|
||||||
|
Support for August lock.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/lock.august/
|
||||||
|
"""
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from homeassistant.components.august import DATA_AUGUST
|
||||||
|
from homeassistant.components.lock import LockDevice
|
||||||
|
from homeassistant.const import ATTR_BATTERY_LEVEL
|
||||||
|
|
||||||
|
DEPENDENCIES = ['august']
|
||||||
|
|
||||||
|
SCAN_INTERVAL = timedelta(seconds=5)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
"""Set up August locks."""
|
||||||
|
data = hass.data[DATA_AUGUST]
|
||||||
|
devices = []
|
||||||
|
|
||||||
|
for lock in data.locks:
|
||||||
|
devices.append(AugustLock(data, lock))
|
||||||
|
|
||||||
|
add_devices(devices, True)
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
def lock(self, **kwargs):
|
||||||
|
"""Lock the device."""
|
||||||
|
self._data.lock(self._lock.device_id)
|
||||||
|
|
||||||
|
def unlock(self, **kwargs):
|
||||||
|
"""Unlock the device."""
|
||||||
|
self._data.unlock(self._lock.device_id)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""Get the latest state of the sensor."""
|
||||||
|
self._lock_status = self._data.get_lock_status(self._lock.device_id)
|
||||||
|
self._lock_detail = self._data.get_lock_detail(self._lock.device_id)
|
||||||
|
|
||||||
|
from august.activity import ActivityType
|
||||||
|
activity = self._data.get_latest_device_activity(
|
||||||
|
self._lock.device_id,
|
||||||
|
ActivityType.LOCK_OPERATION)
|
||||||
|
|
||||||
|
if activity is not None:
|
||||||
|
self._changed_by = activity.operated_by
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Return the name of this device."""
|
||||||
|
return self._lock.device_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_locked(self):
|
||||||
|
"""Return true if device is on."""
|
||||||
|
from august.lock import LockStatus
|
||||||
|
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."""
|
||||||
|
return {
|
||||||
|
ATTR_BATTERY_LEVEL: self._lock_detail.battery_level,
|
||||||
|
}
|
@ -612,6 +612,9 @@ pushetta==1.0.15
|
|||||||
# homeassistant.components.light.rpi_gpio_pwm
|
# homeassistant.components.light.rpi_gpio_pwm
|
||||||
pwmled==1.2.1
|
pwmled==1.2.1
|
||||||
|
|
||||||
|
# homeassistant.components.august
|
||||||
|
py-august==0.3.0
|
||||||
|
|
||||||
# homeassistant.components.canary
|
# homeassistant.components.canary
|
||||||
py-canary==0.4.0
|
py-canary==0.4.0
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user