Added support for lock connected to Verisure system.

This commit is contained in:
turbokongen@hotmail.com 2016-01-27 16:38:43 +01:00 committed by John Arild Berentsen
parent 23c5159f6c
commit 41f908ed39
3 changed files with 148 additions and 12 deletions

View file

@ -17,7 +17,7 @@ from homeassistant.helpers.entity import Entity
from homeassistant.const import (
STATE_LOCKED, STATE_UNLOCKED, STATE_UNKNOWN, SERVICE_LOCK, SERVICE_UNLOCK,
ATTR_ENTITY_ID)
from homeassistant.components import (group, wink)
from homeassistant.components import (group, verisure, wink)
DOMAIN = 'lock'
SCAN_INTERVAL = 30
@ -28,12 +28,15 @@ ENTITY_ID_ALL_LOCKS = group.ENTITY_ID_FORMAT.format('all_locks')
ENTITY_ID_FORMAT = DOMAIN + '.{}'
ATTR_LOCKED = "locked"
ATTR_CODE = 'code'
ATTR_CODE_FORMAT = 'code_format'
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
wink.DISCOVER_LOCKS: 'wink'
wink.DISCOVER_LOCKS: 'wink',
verisure.DISCOVER_LOCKS: 'verisure'
}
_LOGGER = logging.getLogger(__name__)
@ -45,15 +48,25 @@ def is_locked(hass, entity_id=None):
return hass.states.is_state(entity_id, STATE_LOCKED)
def lock(hass, entity_id=None):
def lock(hass, entity_id=None, code=None):
""" Locks all or specified locks. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
data = {}
if code:
data[ATTR_CODE] = code
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_LOCK, data)
def unlock(hass, entity_id=None):
def unlock(hass, entity_id=None, code=None):
""" Unlocks all or specified locks. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
data = {}
if code:
data[ATTR_CODE] = code
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_UNLOCK, data)
@ -68,11 +81,16 @@ def setup(hass, config):
""" Handles calls to the lock services. """
target_locks = component.extract_from_service(service)
if ATTR_CODE not in service.data:
code = None
else:
code = service.data[ATTR_CODE]
for item in target_locks:
if service.service == SERVICE_LOCK:
item.lock()
item.lock(code=code)
else:
item.unlock()
item.unlock(code=code)
if item.should_poll:
item.update_ha_state(True)
@ -91,19 +109,34 @@ class LockDevice(Entity):
""" Represents a lock within Home Assistant. """
# pylint: disable=no-self-use
@property
def code_format(self):
""" regex for code format or None if no code is required. """
return None
@property
def is_locked(self):
""" Is the lock locked or unlocked. """
return None
def lock(self):
def lock(self, **kwargs):
""" Locks the lock. """
raise NotImplementedError()
def unlock(self):
def unlock(self, **kwargs):
""" Unlocks the lock. """
raise NotImplementedError()
@property
def state_attributes(self):
""" Return the state attributes. """
if self.code_format is None:
return None
state_attr = {
ATTR_CODE_FORMAT: self.code_format,
}
return state_attr
@property
def state(self):
locked = self.is_locked

View file

@ -0,0 +1,92 @@
"""
homeassistant.components.lock.verisure
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Interfaces with Verisure locks.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/verisure/
"""
import logging
import homeassistant.components.verisure as verisure
from homeassistant.components.lock import LockDevice
from homeassistant.const import (
STATE_UNKNOWN,
STATE_LOCKED, STATE_UNLOCKED)
_LOGGER = logging.getLogger(__name__)
ATTR_CODE = 'code'
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Verisure platform. """
if not verisure.MY_PAGES:
_LOGGER.error('A connection has not been made to Verisure mypages.')
return False
locks = []
locks.extend([VerisureDoorlock(value)
for value in verisure.LOCK_STATUS.values()
if verisure.SHOW_LOCKS])
add_devices(locks)
# pylint: disable=abstract-method
class VerisureDoorlock(LockDevice):
""" Represents a Verisure doorlock status. """
def __init__(self, lock_status, code=None):
self._id = lock_status.id
self._state = STATE_UNKNOWN
self._code = code
@property
def name(self):
""" Returns the name of the device. """
return 'Lock {}'.format(self._id)
@property
def state(self):
""" Returns the state of the device. """
return self._state
@property
def code_format(self):
""" Six digit code required. """
return '^\\d{%s}$' % verisure.CODE_DIGITS
def update(self):
""" Update lock status """
verisure.update_lock()
if verisure.LOCK_STATUS[self._id].status == 'unlocked':
self._state = STATE_UNLOCKED
elif verisure.LOCK_STATUS[self._id].status == 'locked':
self._state = STATE_LOCKED
elif verisure.LOCK_STATUS[self._id].status != 'pending':
_LOGGER.error(
'Unknown lock state %s',
verisure.LOCK_STATUS[self._id].status)
@property
def is_locked(self):
""" True if device is locked. """
return verisure.LOCK_STATUS[self._id].status
def unlock(self, **kwargs):
""" Send unlock command. """
verisure.MY_PAGES.lock.set(kwargs[ATTR_CODE], self._id, 'UNLOCKED')
_LOGGER.info('verisure doorlock unlocking')
verisure.MY_PAGES.lock.wait_while_pending()
verisure.update_lock()
def lock(self, **kwargs):
""" Send lock command. """
verisure.MY_PAGES.lock.set(kwargs[ATTR_CODE], self._id, 'LOCKED')
_LOGGER.info('verisure doorlock locking')
verisure.MY_PAGES.lock.wait_while_pending()
verisure.update_lock()

View file

@ -26,6 +26,7 @@ DOMAIN = "verisure"
DISCOVER_SENSORS = 'verisure.sensors'
DISCOVER_SWITCHES = 'verisure.switches'
DISCOVER_ALARMS = 'verisure.alarm_control_panel'
DISCOVER_LOCKS = 'verisure.lock'
DEPENDENCIES = ['alarm_control_panel']
REQUIREMENTS = ['vsure==0.5.0']
@ -36,6 +37,7 @@ MY_PAGES = None
ALARM_STATUS = {}
SMARTPLUG_STATUS = {}
CLIMATE_STATUS = {}
LOCK_STATUS = {}
VERISURE_LOGIN_ERROR = None
VERISURE_ERROR = None
@ -44,6 +46,7 @@ SHOW_THERMOMETERS = True
SHOW_HYGROMETERS = True
SHOW_ALARM = True
SHOW_SMARTPLUGS = True
SHOW_LOCKS = True
CODE_DIGITS = 4
# if wrong password was given don't try again
@ -63,11 +66,12 @@ def setup(hass, config):
from verisure import MyPages, LoginError, Error
global SHOW_THERMOMETERS, SHOW_HYGROMETERS,\
SHOW_ALARM, SHOW_SMARTPLUGS, CODE_DIGITS
SHOW_ALARM, SHOW_SMARTPLUGS, SHOW_LOCKS, CODE_DIGITS
SHOW_THERMOMETERS = int(config[DOMAIN].get('thermometers', '1'))
SHOW_HYGROMETERS = int(config[DOMAIN].get('hygrometers', '1'))
SHOW_ALARM = int(config[DOMAIN].get('alarm', '1'))
SHOW_SMARTPLUGS = int(config[DOMAIN].get('smartplugs', '1'))
SHOW_LOCKS = int(config[DOMAIN].get('locks', '1'))
CODE_DIGITS = int(config[DOMAIN].get('code_digits', '4'))
global MY_PAGES
@ -87,11 +91,13 @@ def setup(hass, config):
update_alarm()
update_climate()
update_smartplug()
update_lock()
# Load components for the devices in the ISY controller that we support
for comp_name, discovery in ((('sensor', DISCOVER_SENSORS),
('switch', DISCOVER_SWITCHES),
('alarm_control_panel', DISCOVER_ALARMS))):
('alarm_control_panel', DISCOVER_ALARMS),
('lock', DISCOVER_LOCKS))):
component = get_component(comp_name)
_LOGGER.info(config[DOMAIN])
bootstrap.setup_component(hass, component.DOMAIN, config)
@ -134,6 +140,11 @@ def update_smartplug():
update_component(MY_PAGES.smartplug.get, SMARTPLUG_STATUS)
def update_lock():
""" Updates the status of alarms. """
update_component(MY_PAGES.lock.get, LOCK_STATUS)
def update_component(get_function, status):
""" Updates the status of verisure components. """
if WRONG_PASSWORD_GIVEN: