mirror of
https://github.com/home-assistant/core.git
synced 2025-07-30 02:38:10 +02:00
Allow verisure locks to be configured with a default code (#18873)
* Allow verisure locks to be configured with a default code * linting fix * PR feedback * PR feedback - try harder to prevent future typos A python mock is a magical thing, and will respond to basicaly any method you call on it. It's somewhat better to assert against an explicit variable named 'mock', rather than to assert on the method name you wanted to mock... could prevent a typo from messing up tests. * PR feedback: convert tests to integration-style tests Set up a fake verisure hub, stub out a _lot_ of calls, then test after platform discovery and service calls. It should be noted that we're overriding the `update()` calls in these tests. This was done to prevent even further mocking of the verisure hub's responses. Hopefully, this'll be a foundation for people to write more tests. * more pr feedback
This commit is contained in:
committed by
Martin Hjelmare
parent
eb584a26e2
commit
5ae65142b8
@ -8,7 +8,8 @@ import logging
|
||||
from time import sleep
|
||||
from time import time
|
||||
from homeassistant.components.verisure import HUB as hub
|
||||
from homeassistant.components.verisure import (CONF_LOCKS, CONF_CODE_DIGITS)
|
||||
from homeassistant.components.verisure import (
|
||||
CONF_LOCKS, CONF_DEFAULT_LOCK_CODE, CONF_CODE_DIGITS)
|
||||
from homeassistant.components.lock import LockDevice
|
||||
from homeassistant.const import (
|
||||
ATTR_CODE, STATE_LOCKED, STATE_UNKNOWN, STATE_UNLOCKED)
|
||||
@ -39,6 +40,7 @@ class VerisureDoorlock(LockDevice):
|
||||
self._digits = hub.config.get(CONF_CODE_DIGITS)
|
||||
self._changed_by = None
|
||||
self._change_timestamp = 0
|
||||
self._default_lock_code = hub.config.get(CONF_DEFAULT_LOCK_CODE)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
@ -96,13 +98,25 @@ class VerisureDoorlock(LockDevice):
|
||||
"""Send unlock command."""
|
||||
if self._state == STATE_UNLOCKED:
|
||||
return
|
||||
self.set_lock_state(kwargs[ATTR_CODE], STATE_UNLOCKED)
|
||||
|
||||
code = kwargs.get(ATTR_CODE, self._default_lock_code)
|
||||
if code is None:
|
||||
_LOGGER.error("Code required but none provided")
|
||||
return
|
||||
|
||||
self.set_lock_state(code, STATE_UNLOCKED)
|
||||
|
||||
def lock(self, **kwargs):
|
||||
"""Send lock command."""
|
||||
if self._state == STATE_LOCKED:
|
||||
return
|
||||
self.set_lock_state(kwargs[ATTR_CODE], STATE_LOCKED)
|
||||
|
||||
code = kwargs.get(ATTR_CODE, self._default_lock_code)
|
||||
if code is None:
|
||||
_LOGGER.error("Code required but none provided")
|
||||
return
|
||||
|
||||
self.set_lock_state(code, STATE_LOCKED)
|
||||
|
||||
def set_lock_state(self, code, state):
|
||||
"""Send set lock state command."""
|
||||
|
@ -28,6 +28,7 @@ CONF_DOOR_WINDOW = 'door_window'
|
||||
CONF_GIID = 'giid'
|
||||
CONF_HYDROMETERS = 'hygrometers'
|
||||
CONF_LOCKS = 'locks'
|
||||
CONF_DEFAULT_LOCK_CODE = 'default_lock_code'
|
||||
CONF_MOUSE = 'mouse'
|
||||
CONF_SMARTPLUGS = 'smartplugs'
|
||||
CONF_THERMOMETERS = 'thermometers'
|
||||
@ -52,6 +53,7 @@ CONFIG_SCHEMA = vol.Schema({
|
||||
vol.Optional(CONF_GIID): cv.string,
|
||||
vol.Optional(CONF_HYDROMETERS, default=True): cv.boolean,
|
||||
vol.Optional(CONF_LOCKS, default=True): cv.boolean,
|
||||
vol.Optional(CONF_DEFAULT_LOCK_CODE): cv.string,
|
||||
vol.Optional(CONF_MOUSE, default=True): cv.boolean,
|
||||
vol.Optional(CONF_SMARTPLUGS, default=True): cv.boolean,
|
||||
vol.Optional(CONF_THERMOMETERS, default=True): cv.boolean,
|
||||
|
@ -110,6 +110,9 @@ homematicip==0.9.8
|
||||
# homeassistant.components.sensor.influxdb
|
||||
influxdb==5.2.0
|
||||
|
||||
# homeassistant.components.verisure
|
||||
jsonpath==0.75
|
||||
|
||||
# homeassistant.components.dyson
|
||||
libpurecoollink==0.4.2
|
||||
|
||||
@ -257,6 +260,9 @@ statsd==3.2.1
|
||||
# homeassistant.components.camera.uvc
|
||||
uvcclient==0.11.0
|
||||
|
||||
# homeassistant.components.verisure
|
||||
vsure==1.5.2
|
||||
|
||||
# homeassistant.components.vultr
|
||||
vultr==0.1.2
|
||||
|
||||
|
@ -64,6 +64,7 @@ TEST_REQUIREMENTS = (
|
||||
'home-assistant-frontend',
|
||||
'homematicip',
|
||||
'influxdb',
|
||||
'jsonpath',
|
||||
'libpurecoollink',
|
||||
'libsoundtouch',
|
||||
'luftdaten',
|
||||
@ -110,6 +111,7 @@ TEST_REQUIREMENTS = (
|
||||
'srpenergy',
|
||||
'statsd',
|
||||
'uvcclient',
|
||||
'vsure',
|
||||
'warrant',
|
||||
'pythonwhois',
|
||||
'wakeonlan',
|
||||
|
141
tests/components/lock/test_verisure.py
Normal file
141
tests/components/lock/test_verisure.py
Normal file
@ -0,0 +1,141 @@
|
||||
"""Tests for the Verisure platform."""
|
||||
|
||||
from contextlib import contextmanager
|
||||
from unittest.mock import patch, call
|
||||
from homeassistant.const import STATE_UNLOCKED
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.components.lock import (
|
||||
DOMAIN as LOCK_DOMAIN, SERVICE_LOCK, SERVICE_UNLOCK)
|
||||
from homeassistant.components.verisure import DOMAIN as VERISURE_DOMAIN
|
||||
|
||||
|
||||
NO_DEFAULT_LOCK_CODE_CONFIG = {
|
||||
'verisure': {
|
||||
'username': 'test',
|
||||
'password': 'test',
|
||||
'locks': True,
|
||||
'alarm': False,
|
||||
'door_window': False,
|
||||
'hygrometers': False,
|
||||
'mouse': False,
|
||||
'smartplugs': False,
|
||||
'thermometers': False,
|
||||
'smartcam': False,
|
||||
}
|
||||
}
|
||||
|
||||
DEFAULT_LOCK_CODE_CONFIG = {
|
||||
'verisure': {
|
||||
'username': 'test',
|
||||
'password': 'test',
|
||||
'locks': True,
|
||||
'default_lock_code': '9999',
|
||||
'alarm': False,
|
||||
'door_window': False,
|
||||
'hygrometers': False,
|
||||
'mouse': False,
|
||||
'smartplugs': False,
|
||||
'thermometers': False,
|
||||
'smartcam': False,
|
||||
}
|
||||
}
|
||||
|
||||
LOCKS = ['door_lock']
|
||||
|
||||
|
||||
@contextmanager
|
||||
def mock_hub(config, get_response=LOCKS[0]):
|
||||
"""Extensively mock out a verisure hub."""
|
||||
hub_prefix = 'homeassistant.components.lock.verisure.hub'
|
||||
verisure_prefix = 'verisure.Session'
|
||||
with patch(verisure_prefix) as session, \
|
||||
patch(hub_prefix) as hub:
|
||||
session.login.return_value = True
|
||||
|
||||
hub.config = config['verisure']
|
||||
hub.get.return_value = LOCKS
|
||||
hub.get_first.return_value = get_response.upper()
|
||||
hub.session.set_lock_state.return_value = {
|
||||
'doorLockStateChangeTransactionId': 'test',
|
||||
}
|
||||
hub.session.get_lock_state_transaction.return_value = {
|
||||
'result': 'OK',
|
||||
}
|
||||
|
||||
yield hub
|
||||
|
||||
|
||||
async def setup_verisure_locks(hass, config):
|
||||
"""Set up mock verisure locks."""
|
||||
with mock_hub(config):
|
||||
await async_setup_component(hass, VERISURE_DOMAIN, config)
|
||||
await hass.async_block_till_done()
|
||||
# lock.door_lock, group.all_locks
|
||||
assert len(hass.states.async_all()) == 2
|
||||
|
||||
|
||||
async def test_verisure_no_default_code(hass):
|
||||
"""Test configs without a default lock code."""
|
||||
await setup_verisure_locks(hass, NO_DEFAULT_LOCK_CODE_CONFIG)
|
||||
with mock_hub(NO_DEFAULT_LOCK_CODE_CONFIG,
|
||||
STATE_UNLOCKED) as hub:
|
||||
|
||||
mock = hub.session.set_lock_state
|
||||
await hass.services.async_call(LOCK_DOMAIN, SERVICE_LOCK, {
|
||||
'entity_id': 'lock.door_lock',
|
||||
})
|
||||
await hass.async_block_till_done()
|
||||
assert mock.call_count == 0
|
||||
|
||||
await hass.services.async_call(LOCK_DOMAIN, SERVICE_LOCK, {
|
||||
'entity_id': 'lock.door_lock',
|
||||
'code': '12345',
|
||||
})
|
||||
await hass.async_block_till_done()
|
||||
assert mock.call_args == call('12345', LOCKS[0], 'lock')
|
||||
|
||||
mock.reset_mock()
|
||||
await hass.services.async_call(LOCK_DOMAIN, SERVICE_UNLOCK, {
|
||||
'entity_id': 'lock.door_lock',
|
||||
})
|
||||
await hass.async_block_till_done()
|
||||
assert mock.call_count == 0
|
||||
|
||||
await hass.services.async_call(LOCK_DOMAIN, SERVICE_UNLOCK, {
|
||||
'entity_id': 'lock.door_lock',
|
||||
'code': '12345',
|
||||
})
|
||||
await hass.async_block_till_done()
|
||||
assert mock.call_args == call('12345', LOCKS[0], 'unlock')
|
||||
|
||||
|
||||
async def test_verisure_default_code(hass):
|
||||
"""Test configs with a default lock code."""
|
||||
await setup_verisure_locks(hass, DEFAULT_LOCK_CODE_CONFIG)
|
||||
with mock_hub(DEFAULT_LOCK_CODE_CONFIG, STATE_UNLOCKED) as hub:
|
||||
mock = hub.session.set_lock_state
|
||||
await hass.services.async_call(LOCK_DOMAIN, SERVICE_LOCK, {
|
||||
'entity_id': 'lock.door_lock',
|
||||
})
|
||||
await hass.async_block_till_done()
|
||||
assert mock.call_args == call('9999', LOCKS[0], 'lock')
|
||||
|
||||
await hass.services.async_call(LOCK_DOMAIN, SERVICE_UNLOCK, {
|
||||
'entity_id': 'lock.door_lock',
|
||||
})
|
||||
await hass.async_block_till_done()
|
||||
assert mock.call_args == call('9999', LOCKS[0], 'unlock')
|
||||
|
||||
await hass.services.async_call(LOCK_DOMAIN, SERVICE_LOCK, {
|
||||
'entity_id': 'lock.door_lock',
|
||||
'code': '12345',
|
||||
})
|
||||
await hass.async_block_till_done()
|
||||
assert mock.call_args == call('12345', LOCKS[0], 'lock')
|
||||
|
||||
await hass.services.async_call(LOCK_DOMAIN, SERVICE_UNLOCK, {
|
||||
'entity_id': 'lock.door_lock',
|
||||
'code': '12345',
|
||||
})
|
||||
await hass.async_block_till_done()
|
||||
assert mock.call_args == call('12345', LOCKS[0], 'unlock')
|
Reference in New Issue
Block a user