From 6755ae2605c81f6ad978ddb4794ed59fe0de7324 Mon Sep 17 00:00:00 2001 From: Christoph Gerneth Date: Tue, 12 Jun 2018 12:36:02 +0200 Subject: [PATCH] Add support for KIWI Door Locks (#14485) * initial commit for kiwi door locks bugfixes improved attribute display flake8 more style adjustments * added session handling flake8 * added requirements_all reordered imports and flake8 attempt to pelase a very picky linter also pleasing pylint now :) * re-try the build * added kiwi.py to .coveragerc * reorganized datetime handling and attribute naming * created pypi package for door lock library * updated requirements_all.txt * code review changes * added async lock state reset for locking state * refactored lat/lon attribute updates * initial locked state changed from undefined to locked * refactored is_locked property check * handling authentication exception in setup_platform * added more check in setup_platform * code review changes: return type in setup_platform * fixed logging issue * event handling in main thread * updated kiwiki-client to version 0.1.1 * renamed alias e to exc --- .coveragerc | 1 + homeassistant/components/lock/kiwi.py | 110 ++++++++++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 114 insertions(+) create mode 100644 homeassistant/components/lock/kiwi.py diff --git a/.coveragerc b/.coveragerc index 5a78ec8093f..c4aea0e140a 100644 --- a/.coveragerc +++ b/.coveragerc @@ -470,6 +470,7 @@ omit = homeassistant/components/light/yeelightsunflower.py homeassistant/components/light/zengge.py homeassistant/components/lirc.py + homeassistant/components/lock/kiwi.py homeassistant/components/lock/lockitron.py homeassistant/components/lock/nello.py homeassistant/components/lock/nuki.py diff --git a/homeassistant/components/lock/kiwi.py b/homeassistant/components/lock/kiwi.py new file mode 100644 index 00000000000..78ea45525f2 --- /dev/null +++ b/homeassistant/components/lock/kiwi.py @@ -0,0 +1,110 @@ +""" +Support for the KIWI.KI lock platform. + +For more details about this platform, please refer to the documentation +https://home-assistant.io/components/lock.kiwi/ +""" +import logging + +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.components.lock import (LockDevice, PLATFORM_SCHEMA) +from homeassistant.const import ( + CONF_PASSWORD, CONF_USERNAME, ATTR_ID, ATTR_LONGITUDE, ATTR_LATITUDE, + STATE_LOCKED, STATE_UNLOCKED) +from homeassistant.helpers.event import async_call_later +from homeassistant.core import callback + +REQUIREMENTS = ['kiwiki-client==0.1.1'] + +_LOGGER = logging.getLogger(__name__) + +ATTR_TYPE = 'hardware_type' +ATTR_PERMISSION = 'permission' +ATTR_CAN_INVITE = 'can_invite_others' + +UNLOCK_MAINTAIN_TIME = 5 + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up the KIWI lock platform.""" + from kiwiki import KiwiClient, KiwiException + try: + kiwi = KiwiClient(config[CONF_USERNAME], config[CONF_PASSWORD]) + except KiwiException as exc: + _LOGGER.error(exc) + return + available_locks = kiwi.get_locks() + if not available_locks: + # No locks found; abort setup routine. + _LOGGER.info("No KIWI locks found in your account.") + return + add_devices([KiwiLock(lock, kiwi) for lock in available_locks], True) + + +class KiwiLock(LockDevice): + """Representation of a Kiwi lock.""" + + def __init__(self, kiwi_lock, client): + """Initialize the lock.""" + self._sensor = kiwi_lock + self._client = client + self.lock_id = kiwi_lock['sensor_id'] + self._state = STATE_LOCKED + + address = kiwi_lock.get('address') + address.update({ + ATTR_LATITUDE: address.pop('lat', None), + ATTR_LONGITUDE: address.pop('lng', None) + }) + + self._device_attrs = { + ATTR_ID: self.lock_id, + ATTR_TYPE: kiwi_lock.get('hardware_type'), + ATTR_PERMISSION: kiwi_lock.get('highest_permission'), + ATTR_CAN_INVITE: kiwi_lock.get('can_invite'), + **address + } + + @property + def name(self): + """Return the name of the lock.""" + name = self._sensor.get('name') + specifier = self._sensor['address'].get('specifier') + return name or specifier + + @property + def is_locked(self): + """Return true if lock is locked.""" + return self._state == STATE_LOCKED + + @property + def device_state_attributes(self): + """Return the device specific state attributes.""" + return self._device_attrs + + @callback + def clear_unlock_state(self, _): + """Clear unlock state automatically.""" + self._state = STATE_LOCKED + self.async_schedule_update_ha_state() + + def unlock(self, **kwargs): + """Unlock the device.""" + from kiwiki import KiwiException + try: + self._client.open_door(self.lock_id) + except KiwiException: + _LOGGER.error("failed to open door") + else: + self._state = STATE_UNLOCKED + self.hass.add_job( + async_call_later, self.hass, UNLOCK_MAINTAIN_TIME, + self.clear_unlock_state + ) diff --git a/requirements_all.txt b/requirements_all.txt index 1a524c9fd0f..aa346e66ef1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -482,6 +482,9 @@ keyring==12.2.1 # homeassistant.scripts.keyring keyrings.alt==3.1 +# homeassistant.components.lock.kiwi +kiwiki-client==0.1.1 + # homeassistant.components.konnected konnected==0.1.2