mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 12:17:07 +00:00
Merge pull request #1136 from turbokongen/verisure-locks
Added support for lock connected to Verisure system.
This commit is contained in:
commit
09b3aba51b
@ -17,7 +17,7 @@ from homeassistant.helpers.entity import Entity
|
|||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
STATE_LOCKED, STATE_UNLOCKED, STATE_UNKNOWN, SERVICE_LOCK, SERVICE_UNLOCK,
|
STATE_LOCKED, STATE_UNLOCKED, STATE_UNKNOWN, SERVICE_LOCK, SERVICE_UNLOCK,
|
||||||
ATTR_ENTITY_ID)
|
ATTR_ENTITY_ID)
|
||||||
from homeassistant.components import (group, wink)
|
from homeassistant.components import (group, verisure, wink)
|
||||||
|
|
||||||
DOMAIN = 'lock'
|
DOMAIN = 'lock'
|
||||||
SCAN_INTERVAL = 30
|
SCAN_INTERVAL = 30
|
||||||
@ -28,12 +28,15 @@ ENTITY_ID_ALL_LOCKS = group.ENTITY_ID_FORMAT.format('all_locks')
|
|||||||
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
||||||
|
|
||||||
ATTR_LOCKED = "locked"
|
ATTR_LOCKED = "locked"
|
||||||
|
ATTR_CODE = 'code'
|
||||||
|
ATTR_CODE_FORMAT = 'code_format'
|
||||||
|
|
||||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
|
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
|
||||||
|
|
||||||
# Maps discovered services to their platforms
|
# Maps discovered services to their platforms
|
||||||
DISCOVERY_PLATFORMS = {
|
DISCOVERY_PLATFORMS = {
|
||||||
wink.DISCOVER_LOCKS: 'wink'
|
wink.DISCOVER_LOCKS: 'wink',
|
||||||
|
verisure.DISCOVER_LOCKS: 'verisure'
|
||||||
}
|
}
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -45,15 +48,25 @@ def is_locked(hass, entity_id=None):
|
|||||||
return hass.states.is_state(entity_id, STATE_LOCKED)
|
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. """
|
""" 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)
|
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. """
|
""" 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)
|
hass.services.call(DOMAIN, SERVICE_UNLOCK, data)
|
||||||
|
|
||||||
|
|
||||||
@ -68,11 +81,16 @@ def setup(hass, config):
|
|||||||
""" Handles calls to the lock services. """
|
""" Handles calls to the lock services. """
|
||||||
target_locks = component.extract_from_service(service)
|
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:
|
for item in target_locks:
|
||||||
if service.service == SERVICE_LOCK:
|
if service.service == SERVICE_LOCK:
|
||||||
item.lock()
|
item.lock(code=code)
|
||||||
else:
|
else:
|
||||||
item.unlock()
|
item.unlock(code=code)
|
||||||
|
|
||||||
if item.should_poll:
|
if item.should_poll:
|
||||||
item.update_ha_state(True)
|
item.update_ha_state(True)
|
||||||
@ -91,19 +109,34 @@ class LockDevice(Entity):
|
|||||||
""" Represents a lock within Home Assistant. """
|
""" Represents a lock within Home Assistant. """
|
||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
|
|
||||||
|
@property
|
||||||
|
def code_format(self):
|
||||||
|
""" regex for code format or None if no code is required. """
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_locked(self):
|
def is_locked(self):
|
||||||
""" Is the lock locked or unlocked. """
|
""" Is the lock locked or unlocked. """
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def lock(self):
|
def lock(self, **kwargs):
|
||||||
""" Locks the lock. """
|
""" Locks the lock. """
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def unlock(self):
|
def unlock(self, **kwargs):
|
||||||
""" Unlocks the lock. """
|
""" Unlocks the lock. """
|
||||||
raise NotImplementedError()
|
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
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
locked = self.is_locked
|
locked = self.is_locked
|
||||||
|
92
homeassistant/components/lock/verisure.py
Normal file
92
homeassistant/components/lock/verisure.py
Normal 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()
|
@ -26,6 +26,7 @@ DOMAIN = "verisure"
|
|||||||
DISCOVER_SENSORS = 'verisure.sensors'
|
DISCOVER_SENSORS = 'verisure.sensors'
|
||||||
DISCOVER_SWITCHES = 'verisure.switches'
|
DISCOVER_SWITCHES = 'verisure.switches'
|
||||||
DISCOVER_ALARMS = 'verisure.alarm_control_panel'
|
DISCOVER_ALARMS = 'verisure.alarm_control_panel'
|
||||||
|
DISCOVER_LOCKS = 'verisure.lock'
|
||||||
|
|
||||||
DEPENDENCIES = ['alarm_control_panel']
|
DEPENDENCIES = ['alarm_control_panel']
|
||||||
REQUIREMENTS = ['vsure==0.5.0']
|
REQUIREMENTS = ['vsure==0.5.0']
|
||||||
@ -36,6 +37,7 @@ MY_PAGES = None
|
|||||||
ALARM_STATUS = {}
|
ALARM_STATUS = {}
|
||||||
SMARTPLUG_STATUS = {}
|
SMARTPLUG_STATUS = {}
|
||||||
CLIMATE_STATUS = {}
|
CLIMATE_STATUS = {}
|
||||||
|
LOCK_STATUS = {}
|
||||||
|
|
||||||
VERISURE_LOGIN_ERROR = None
|
VERISURE_LOGIN_ERROR = None
|
||||||
VERISURE_ERROR = None
|
VERISURE_ERROR = None
|
||||||
@ -44,6 +46,7 @@ SHOW_THERMOMETERS = True
|
|||||||
SHOW_HYGROMETERS = True
|
SHOW_HYGROMETERS = True
|
||||||
SHOW_ALARM = True
|
SHOW_ALARM = True
|
||||||
SHOW_SMARTPLUGS = True
|
SHOW_SMARTPLUGS = True
|
||||||
|
SHOW_LOCKS = True
|
||||||
CODE_DIGITS = 4
|
CODE_DIGITS = 4
|
||||||
|
|
||||||
# if wrong password was given don't try again
|
# if wrong password was given don't try again
|
||||||
@ -63,11 +66,12 @@ def setup(hass, config):
|
|||||||
from verisure import MyPages, LoginError, Error
|
from verisure import MyPages, LoginError, Error
|
||||||
|
|
||||||
global SHOW_THERMOMETERS, SHOW_HYGROMETERS,\
|
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_THERMOMETERS = int(config[DOMAIN].get('thermometers', '1'))
|
||||||
SHOW_HYGROMETERS = int(config[DOMAIN].get('hygrometers', '1'))
|
SHOW_HYGROMETERS = int(config[DOMAIN].get('hygrometers', '1'))
|
||||||
SHOW_ALARM = int(config[DOMAIN].get('alarm', '1'))
|
SHOW_ALARM = int(config[DOMAIN].get('alarm', '1'))
|
||||||
SHOW_SMARTPLUGS = int(config[DOMAIN].get('smartplugs', '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'))
|
CODE_DIGITS = int(config[DOMAIN].get('code_digits', '4'))
|
||||||
|
|
||||||
global MY_PAGES
|
global MY_PAGES
|
||||||
@ -87,11 +91,13 @@ def setup(hass, config):
|
|||||||
update_alarm()
|
update_alarm()
|
||||||
update_climate()
|
update_climate()
|
||||||
update_smartplug()
|
update_smartplug()
|
||||||
|
update_lock()
|
||||||
|
|
||||||
# Load components for the devices in the ISY controller that we support
|
# Load components for the devices in the ISY controller that we support
|
||||||
for comp_name, discovery in ((('sensor', DISCOVER_SENSORS),
|
for comp_name, discovery in ((('sensor', DISCOVER_SENSORS),
|
||||||
('switch', DISCOVER_SWITCHES),
|
('switch', DISCOVER_SWITCHES),
|
||||||
('alarm_control_panel', DISCOVER_ALARMS))):
|
('alarm_control_panel', DISCOVER_ALARMS),
|
||||||
|
('lock', DISCOVER_LOCKS))):
|
||||||
component = get_component(comp_name)
|
component = get_component(comp_name)
|
||||||
_LOGGER.info(config[DOMAIN])
|
_LOGGER.info(config[DOMAIN])
|
||||||
bootstrap.setup_component(hass, component.DOMAIN, config)
|
bootstrap.setup_component(hass, component.DOMAIN, config)
|
||||||
@ -134,6 +140,11 @@ def update_smartplug():
|
|||||||
update_component(MY_PAGES.smartplug.get, SMARTPLUG_STATUS)
|
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):
|
def update_component(get_function, status):
|
||||||
""" Updates the status of verisure components. """
|
""" Updates the status of verisure components. """
|
||||||
if WRONG_PASSWORD_GIVEN:
|
if WRONG_PASSWORD_GIVEN:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user