mirror of
https://github.com/home-assistant/core.git
synced 2025-07-20 19:57:07 +00:00
ZWave Lock Tests (#6730)
* ZWave Lock Tests * Linting fixes * Missed coveragerc
This commit is contained in:
parent
978b539111
commit
e7425e9808
@ -231,7 +231,6 @@ omit =
|
|||||||
homeassistant/components/light/zengge.py
|
homeassistant/components/light/zengge.py
|
||||||
homeassistant/components/lirc.py
|
homeassistant/components/lirc.py
|
||||||
homeassistant/components/lock/nuki.py
|
homeassistant/components/lock/nuki.py
|
||||||
homeassistant/components/lock/zwave.py
|
|
||||||
homeassistant/components/media_player/anthemav.py
|
homeassistant/components/media_player/anthemav.py
|
||||||
homeassistant/components/media_player/apple_tv.py
|
homeassistant/components/media_player/apple_tv.py
|
||||||
homeassistant/components/media_player/aquostv.py
|
homeassistant/components/media_player/aquostv.py
|
||||||
|
@ -6,6 +6,7 @@ https://home-assistant.io/components/lock.zwave/
|
|||||||
"""
|
"""
|
||||||
# Because we do not compile openzwave on CI
|
# Because we do not compile openzwave on CI
|
||||||
# pylint: disable=import-error
|
# pylint: disable=import-error
|
||||||
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
@ -13,7 +14,6 @@ import voluptuous as vol
|
|||||||
|
|
||||||
from homeassistant.components.lock import DOMAIN, LockDevice
|
from homeassistant.components.lock import DOMAIN, LockDevice
|
||||||
from homeassistant.components import zwave
|
from homeassistant.components import zwave
|
||||||
from homeassistant.components.zwave import async_setup_platform # noqa # pylint: disable=unused-import
|
|
||||||
from homeassistant.config import load_yaml_config_file
|
from homeassistant.config import load_yaml_config_file
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ LOCK_ALARM_TYPE = {
|
|||||||
'9': 'Deadbolt Jammed',
|
'9': 'Deadbolt Jammed',
|
||||||
'18': 'Locked with Keypad by user ',
|
'18': 'Locked with Keypad by user ',
|
||||||
'19': 'Unlocked with Keypad by user ',
|
'19': 'Unlocked with Keypad by user ',
|
||||||
'21': 'Manually Locked by',
|
'21': 'Manually Locked by ',
|
||||||
'22': 'Manually Unlocked by Key or Inside thumb turn',
|
'22': 'Manually Unlocked by Key or Inside thumb turn',
|
||||||
'24': 'Locked by RF',
|
'24': 'Locked by RF',
|
||||||
'25': 'Unlocked by RF',
|
'25': 'Unlocked by RF',
|
||||||
@ -120,8 +120,12 @@ CLEAR_USERCODE_SCHEMA = vol.Schema({
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
def get_device(hass, node, values, **kwargs):
|
@asyncio.coroutine
|
||||||
"""Create zwave entity device."""
|
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||||
|
"""Generic Z-Wave platform setup."""
|
||||||
|
yield from zwave.async_setup_platform(
|
||||||
|
hass, config, async_add_devices, discovery_info)
|
||||||
|
|
||||||
descriptions = load_yaml_config_file(
|
descriptions = load_yaml_config_file(
|
||||||
path.join(path.dirname(__file__), 'services.yaml'))
|
path.join(path.dirname(__file__), 'services.yaml'))
|
||||||
|
|
||||||
@ -140,6 +144,7 @@ def get_device(hass, node, values, **kwargs):
|
|||||||
_LOGGER.error('Invalid code provided: (%s)'
|
_LOGGER.error('Invalid code provided: (%s)'
|
||||||
' usercode must %s or less digits',
|
' usercode must %s or less digits',
|
||||||
usercode, len(value.data))
|
usercode, len(value.data))
|
||||||
|
break
|
||||||
value.data = str(usercode)
|
value.data = str(usercode)
|
||||||
break
|
break
|
||||||
|
|
||||||
@ -175,22 +180,25 @@ def get_device(hass, node, values, **kwargs):
|
|||||||
_LOGGER.info('Usercode at slot %s is cleared', value.index)
|
_LOGGER.info('Usercode at slot %s is cleared', value.index)
|
||||||
break
|
break
|
||||||
|
|
||||||
if node.has_command_class(zwave.const.COMMAND_CLASS_USER_CODE):
|
hass.services.async_register(DOMAIN,
|
||||||
hass.services.register(DOMAIN,
|
|
||||||
SERVICE_SET_USERCODE,
|
SERVICE_SET_USERCODE,
|
||||||
set_usercode,
|
set_usercode,
|
||||||
descriptions.get(SERVICE_SET_USERCODE),
|
descriptions.get(SERVICE_SET_USERCODE),
|
||||||
schema=SET_USERCODE_SCHEMA)
|
schema=SET_USERCODE_SCHEMA)
|
||||||
hass.services.register(DOMAIN,
|
hass.services.async_register(DOMAIN,
|
||||||
SERVICE_GET_USERCODE,
|
SERVICE_GET_USERCODE,
|
||||||
get_usercode,
|
get_usercode,
|
||||||
descriptions.get(SERVICE_GET_USERCODE),
|
descriptions.get(SERVICE_GET_USERCODE),
|
||||||
schema=GET_USERCODE_SCHEMA)
|
schema=GET_USERCODE_SCHEMA)
|
||||||
hass.services.register(DOMAIN,
|
hass.services.async_register(DOMAIN,
|
||||||
SERVICE_CLEAR_USERCODE,
|
SERVICE_CLEAR_USERCODE,
|
||||||
clear_usercode,
|
clear_usercode,
|
||||||
descriptions.get(SERVICE_CLEAR_USERCODE),
|
descriptions.get(SERVICE_CLEAR_USERCODE),
|
||||||
schema=CLEAR_USERCODE_SCHEMA)
|
schema=CLEAR_USERCODE_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
def get_device(node, values, **kwargs):
|
||||||
|
"""Create zwave entity device."""
|
||||||
return ZwaveLock(values)
|
return ZwaveLock(values)
|
||||||
|
|
||||||
|
|
||||||
@ -253,7 +261,8 @@ class ZwaveLock(zwave.ZWaveDeviceEntity, LockDevice):
|
|||||||
self._lock_status = '{}{}'.format(
|
self._lock_status = '{}{}'.format(
|
||||||
LOCK_ALARM_TYPE.get(str(alarm_type)),
|
LOCK_ALARM_TYPE.get(str(alarm_type)),
|
||||||
MANUAL_LOCK_ALARM_LEVEL.get(str(alarm_level)))
|
MANUAL_LOCK_ALARM_LEVEL.get(str(alarm_level)))
|
||||||
if alarm_type in ALARM_TYPE_STD:
|
return
|
||||||
|
if str(alarm_type) in ALARM_TYPE_STD:
|
||||||
self._lock_status = '{}{}'.format(
|
self._lock_status = '{}{}'.format(
|
||||||
LOCK_ALARM_TYPE.get(str(alarm_type)), str(alarm_level))
|
LOCK_ALARM_TYPE.get(str(alarm_type)), str(alarm_level))
|
||||||
return
|
return
|
||||||
|
271
tests/components/lock/test_zwave.py
Normal file
271
tests/components/lock/test_zwave.py
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
"""Test Z-Wave locks."""
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from unittest.mock import patch, MagicMock
|
||||||
|
|
||||||
|
from homeassistant.components.lock import zwave
|
||||||
|
from homeassistant.components.zwave import const
|
||||||
|
|
||||||
|
from tests.mock.zwave import (
|
||||||
|
MockNode, MockValue, MockEntityValues, value_changed)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_device_detects_lock(mock_openzwave):
|
||||||
|
"""Test get_device returns a Z-Wave lock."""
|
||||||
|
node = MockNode()
|
||||||
|
values = MockEntityValues(
|
||||||
|
primary=MockValue(data=None, node=node),
|
||||||
|
access_control=None,
|
||||||
|
alarm_type=None,
|
||||||
|
alarm_level=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
device = zwave.get_device(node=node, values=values, node_config={})
|
||||||
|
assert isinstance(device, zwave.ZwaveLock)
|
||||||
|
|
||||||
|
|
||||||
|
def test_lock_turn_on_and_off(mock_openzwave):
|
||||||
|
"""Test turning on a Z-Wave lock."""
|
||||||
|
node = MockNode()
|
||||||
|
values = MockEntityValues(
|
||||||
|
primary=MockValue(data=None, node=node),
|
||||||
|
access_control=None,
|
||||||
|
alarm_type=None,
|
||||||
|
alarm_level=None,
|
||||||
|
)
|
||||||
|
device = zwave.get_device(node=node, values=values, node_config={})
|
||||||
|
|
||||||
|
assert not values.primary.data
|
||||||
|
|
||||||
|
device.lock()
|
||||||
|
assert values.primary.data
|
||||||
|
|
||||||
|
device.unlock()
|
||||||
|
assert not values.primary.data
|
||||||
|
|
||||||
|
|
||||||
|
def test_lock_value_changed(mock_openzwave):
|
||||||
|
"""Test value changed for Z-Wave lock."""
|
||||||
|
node = MockNode()
|
||||||
|
values = MockEntityValues(
|
||||||
|
primary=MockValue(data=None, node=node),
|
||||||
|
access_control=None,
|
||||||
|
alarm_type=None,
|
||||||
|
alarm_level=None,
|
||||||
|
)
|
||||||
|
device = zwave.get_device(node=node, values=values, node_config={})
|
||||||
|
|
||||||
|
assert not device.is_locked
|
||||||
|
|
||||||
|
values.primary.data = True
|
||||||
|
value_changed(values.primary)
|
||||||
|
|
||||||
|
assert device.is_locked
|
||||||
|
|
||||||
|
|
||||||
|
def test_v2btze_value_changed(mock_openzwave):
|
||||||
|
"""Test value changed for v2btze Z-Wave lock."""
|
||||||
|
node = MockNode(manufacturer_id='010e', product_id='0002')
|
||||||
|
values = MockEntityValues(
|
||||||
|
primary=MockValue(data=None, node=node),
|
||||||
|
v2btze_advanced=MockValue(data='Advanced', node=node),
|
||||||
|
access_control=MockValue(data=19, node=node),
|
||||||
|
alarm_type=None,
|
||||||
|
alarm_level=None,
|
||||||
|
)
|
||||||
|
device = zwave.get_device(node=node, values=values, node_config={})
|
||||||
|
assert device._v2btze
|
||||||
|
|
||||||
|
assert not device.is_locked
|
||||||
|
|
||||||
|
values.access_control.data = 24
|
||||||
|
value_changed(values.primary)
|
||||||
|
|
||||||
|
assert device.is_locked
|
||||||
|
|
||||||
|
|
||||||
|
def test_lock_access_control(mock_openzwave):
|
||||||
|
"""Test access control for Z-Wave lock."""
|
||||||
|
node = MockNode()
|
||||||
|
values = MockEntityValues(
|
||||||
|
primary=MockValue(data=None, node=node),
|
||||||
|
access_control=MockValue(data=11, node=node),
|
||||||
|
alarm_type=None,
|
||||||
|
alarm_level=None,
|
||||||
|
)
|
||||||
|
device = zwave.get_device(node=node, values=values, node_config={})
|
||||||
|
|
||||||
|
assert device.device_state_attributes[zwave.ATTR_NOTIFICATION] == \
|
||||||
|
'Lock Jammed'
|
||||||
|
|
||||||
|
|
||||||
|
def test_lock_alarm_type(mock_openzwave):
|
||||||
|
"""Test alarm type for Z-Wave lock."""
|
||||||
|
node = MockNode()
|
||||||
|
values = MockEntityValues(
|
||||||
|
primary=MockValue(data=None, node=node),
|
||||||
|
access_control=None,
|
||||||
|
alarm_type=MockValue(data=None, node=node),
|
||||||
|
alarm_level=None,
|
||||||
|
)
|
||||||
|
device = zwave.get_device(node=node, values=values, node_config={})
|
||||||
|
|
||||||
|
assert zwave.ATTR_LOCK_STATUS not in device.device_state_attributes
|
||||||
|
|
||||||
|
values.alarm_type.data = 21
|
||||||
|
value_changed(values.alarm_type)
|
||||||
|
assert device.device_state_attributes[zwave.ATTR_LOCK_STATUS] == \
|
||||||
|
'Manually Locked by None'
|
||||||
|
|
||||||
|
values.alarm_type.data = 18
|
||||||
|
value_changed(values.alarm_type)
|
||||||
|
assert device.device_state_attributes[zwave.ATTR_LOCK_STATUS] == \
|
||||||
|
'Locked with Keypad by user None'
|
||||||
|
|
||||||
|
values.alarm_type.data = 161
|
||||||
|
value_changed(values.alarm_type)
|
||||||
|
assert device.device_state_attributes[zwave.ATTR_LOCK_STATUS] == \
|
||||||
|
'Tamper Alarm: None'
|
||||||
|
|
||||||
|
values.alarm_type.data = 9
|
||||||
|
value_changed(values.alarm_type)
|
||||||
|
assert device.device_state_attributes[zwave.ATTR_LOCK_STATUS] == \
|
||||||
|
'Deadbolt Jammed'
|
||||||
|
|
||||||
|
|
||||||
|
def test_lock_alarm_level(mock_openzwave):
|
||||||
|
"""Test alarm level for Z-Wave lock."""
|
||||||
|
node = MockNode()
|
||||||
|
values = MockEntityValues(
|
||||||
|
primary=MockValue(data=None, node=node),
|
||||||
|
access_control=None,
|
||||||
|
alarm_type=MockValue(data=None, node=node),
|
||||||
|
alarm_level=MockValue(data=None, node=node),
|
||||||
|
)
|
||||||
|
device = zwave.get_device(node=node, values=values, node_config={})
|
||||||
|
|
||||||
|
assert zwave.ATTR_LOCK_STATUS not in device.device_state_attributes
|
||||||
|
|
||||||
|
values.alarm_type.data = 21
|
||||||
|
values.alarm_level.data = 1
|
||||||
|
value_changed(values.alarm_type)
|
||||||
|
value_changed(values.alarm_level)
|
||||||
|
assert device.device_state_attributes[zwave.ATTR_LOCK_STATUS] == \
|
||||||
|
'Manually Locked by Key Cylinder or Inside thumb turn'
|
||||||
|
|
||||||
|
values.alarm_type.data = 18
|
||||||
|
values.alarm_level.data = 'alice'
|
||||||
|
value_changed(values.alarm_type)
|
||||||
|
value_changed(values.alarm_level)
|
||||||
|
assert device.device_state_attributes[zwave.ATTR_LOCK_STATUS] == \
|
||||||
|
'Locked with Keypad by user alice'
|
||||||
|
|
||||||
|
values.alarm_type.data = 161
|
||||||
|
values.alarm_level.data = 1
|
||||||
|
value_changed(values.alarm_type)
|
||||||
|
value_changed(values.alarm_level)
|
||||||
|
assert device.device_state_attributes[zwave.ATTR_LOCK_STATUS] == \
|
||||||
|
'Tamper Alarm: Too many keypresses'
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_lock_set_usercode_service(hass, mock_openzwave):
|
||||||
|
"""Test the zwave lock set_usercode service."""
|
||||||
|
node = MockNode(node_id=12)
|
||||||
|
value0 = MockValue(data=None, node=node, index=0)
|
||||||
|
value1 = MockValue(data=None, node=node, index=1)
|
||||||
|
yield from zwave.async_setup_platform(
|
||||||
|
hass, {}, MagicMock())
|
||||||
|
|
||||||
|
node.get_values.return_value = {
|
||||||
|
value0.value_id: value0,
|
||||||
|
value1.value_id: value1,
|
||||||
|
}
|
||||||
|
|
||||||
|
with patch.object(zwave.zwave, 'NETWORK') as mock_network:
|
||||||
|
mock_network.nodes = {
|
||||||
|
node.node_id: node
|
||||||
|
}
|
||||||
|
yield from hass.services.async_call(
|
||||||
|
zwave.DOMAIN, zwave.SERVICE_SET_USERCODE, {
|
||||||
|
const.ATTR_NODE_ID: node.node_id,
|
||||||
|
zwave.ATTR_USERCODE: '1234',
|
||||||
|
zwave.ATTR_CODE_SLOT: 1,
|
||||||
|
})
|
||||||
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert value1.data == '1234'
|
||||||
|
|
||||||
|
with patch.object(zwave.zwave, 'NETWORK') as mock_network:
|
||||||
|
mock_network.nodes = {
|
||||||
|
node.node_id: node
|
||||||
|
}
|
||||||
|
yield from hass.services.async_call(
|
||||||
|
zwave.DOMAIN, zwave.SERVICE_SET_USERCODE, {
|
||||||
|
const.ATTR_NODE_ID: node.node_id,
|
||||||
|
zwave.ATTR_USERCODE: '12345',
|
||||||
|
zwave.ATTR_CODE_SLOT: 1,
|
||||||
|
})
|
||||||
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert value1.data == '1234'
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_lock_get_usercode_service(hass, mock_openzwave):
|
||||||
|
"""Test the zwave lock get_usercode service."""
|
||||||
|
node = MockNode(node_id=12)
|
||||||
|
value0 = MockValue(data=None, node=node, index=0)
|
||||||
|
value1 = MockValue(data='1234', node=node, index=1)
|
||||||
|
yield from zwave.async_setup_platform(
|
||||||
|
hass, {}, MagicMock())
|
||||||
|
|
||||||
|
node.get_values.return_value = {
|
||||||
|
value0.value_id: value0,
|
||||||
|
value1.value_id: value1,
|
||||||
|
}
|
||||||
|
|
||||||
|
with patch.object(zwave.zwave, 'NETWORK') as mock_network:
|
||||||
|
with patch.object(zwave, '_LOGGER') as mock_logger:
|
||||||
|
mock_network.nodes = {
|
||||||
|
node.node_id: node
|
||||||
|
}
|
||||||
|
yield from hass.services.async_call(
|
||||||
|
zwave.DOMAIN, zwave.SERVICE_GET_USERCODE, {
|
||||||
|
const.ATTR_NODE_ID: node.node_id,
|
||||||
|
zwave.ATTR_CODE_SLOT: 1,
|
||||||
|
})
|
||||||
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
|
# This service only seems to write to the log
|
||||||
|
assert mock_logger.info.called
|
||||||
|
assert len(mock_logger.info.mock_calls) == 1
|
||||||
|
assert mock_logger.info.mock_calls[0][1][2] == '1234'
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_lock_clear_usercode_service(hass, mock_openzwave):
|
||||||
|
"""Test the zwave lock clear_usercode service."""
|
||||||
|
node = MockNode(node_id=12)
|
||||||
|
value0 = MockValue(data=None, node=node, index=0)
|
||||||
|
value1 = MockValue(data='123', node=node, index=1)
|
||||||
|
yield from zwave.async_setup_platform(
|
||||||
|
hass, {}, MagicMock())
|
||||||
|
|
||||||
|
node.get_values.return_value = {
|
||||||
|
value0.value_id: value0,
|
||||||
|
value1.value_id: value1,
|
||||||
|
}
|
||||||
|
|
||||||
|
with patch.object(zwave.zwave, 'NETWORK') as mock_network:
|
||||||
|
mock_network.nodes = {
|
||||||
|
node.node_id: node
|
||||||
|
}
|
||||||
|
yield from hass.services.async_call(
|
||||||
|
zwave.DOMAIN, zwave.SERVICE_CLEAR_USERCODE, {
|
||||||
|
const.ATTR_NODE_ID: node.node_id,
|
||||||
|
zwave.ATTR_CODE_SLOT: 1
|
||||||
|
})
|
||||||
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert value1.data == '\0\0\0'
|
@ -67,7 +67,7 @@ class MockValue(MagicMock):
|
|||||||
self.data_items = data_items
|
self.data_items = data_items
|
||||||
self.node = node
|
self.node = node
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
self.index = 0
|
self.index = index
|
||||||
self.command_class = command_class
|
self.command_class = command_class
|
||||||
self.units = units
|
self.units = units
|
||||||
self.type = type
|
self.type = type
|
||||||
|
Loading…
x
Reference in New Issue
Block a user