-
-
Notifications
You must be signed in to change notification settings - Fork 37.8k
ZWave Lock Tests #6730
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ZWave Lock Tests #6730
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,14 +6,14 @@ | |
| """ | ||
| # Because we do not compile openzwave on CI | ||
| # pylint: disable=import-error | ||
| import asyncio | ||
| import logging | ||
| from os import path | ||
|
|
||
| import voluptuous as vol | ||
|
|
||
| from homeassistant.components.lock import DOMAIN, LockDevice | ||
| 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 | ||
| import homeassistant.helpers.config_validation as cv | ||
|
|
||
|
|
@@ -53,7 +53,7 @@ | |
| '9': 'Deadbolt Jammed', | ||
| '18': 'Locked 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', | ||
| '24': 'Locked by RF', | ||
| '25': 'Unlocked by RF', | ||
|
|
@@ -120,8 +120,12 @@ | |
| }) | ||
|
|
||
|
|
||
| def get_device(hass, node, values, **kwargs): | ||
| """Create zwave entity device.""" | ||
| @asyncio.coroutine | ||
| 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( | ||
| path.join(path.dirname(__file__), 'services.yaml')) | ||
|
|
||
|
|
@@ -140,6 +144,7 @@ def set_usercode(service): | |
| _LOGGER.error('Invalid code provided: (%s)' | ||
| ' usercode must %s or less digits', | ||
| usercode, len(value.data)) | ||
| break | ||
| value.data = str(usercode) | ||
| break | ||
|
|
||
|
|
@@ -175,22 +180,25 @@ def clear_usercode(service): | |
| _LOGGER.info('Usercode at slot %s is cleared', value.index) | ||
| break | ||
|
|
||
| if node.has_command_class(zwave.const.COMMAND_CLASS_USER_CODE): | ||
| hass.services.register(DOMAIN, | ||
| SERVICE_SET_USERCODE, | ||
| set_usercode, | ||
| descriptions.get(SERVICE_SET_USERCODE), | ||
| schema=SET_USERCODE_SCHEMA) | ||
| hass.services.register(DOMAIN, | ||
| SERVICE_GET_USERCODE, | ||
| get_usercode, | ||
| descriptions.get(SERVICE_GET_USERCODE), | ||
| schema=GET_USERCODE_SCHEMA) | ||
| hass.services.register(DOMAIN, | ||
| SERVICE_CLEAR_USERCODE, | ||
| clear_usercode, | ||
| descriptions.get(SERVICE_CLEAR_USERCODE), | ||
| schema=CLEAR_USERCODE_SCHEMA) | ||
| hass.services.async_register(DOMAIN, | ||
| SERVICE_SET_USERCODE, | ||
| set_usercode, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. continuation line under-indented for visual indent |
||
| descriptions.get(SERVICE_SET_USERCODE), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. continuation line under-indented for visual indent |
||
| schema=SET_USERCODE_SCHEMA) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. continuation line under-indented for visual indent |
||
| hass.services.async_register(DOMAIN, | ||
| SERVICE_GET_USERCODE, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. continuation line under-indented for visual indent |
||
| get_usercode, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. continuation line under-indented for visual indent |
||
| descriptions.get(SERVICE_GET_USERCODE), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. continuation line under-indented for visual indent |
||
| schema=GET_USERCODE_SCHEMA) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. continuation line under-indented for visual indent |
||
| hass.services.async_register(DOMAIN, | ||
| SERVICE_CLEAR_USERCODE, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. continuation line under-indented for visual indent |
||
| clear_usercode, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. continuation line under-indented for visual indent |
||
| descriptions.get(SERVICE_CLEAR_USERCODE), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. continuation line under-indented for visual indent |
||
| schema=CLEAR_USERCODE_SCHEMA) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. continuation line under-indented for visual indent |
||
|
|
||
|
|
||
| def get_device(node, values, **kwargs): | ||
| """Create zwave entity device.""" | ||
| return ZwaveLock(values) | ||
|
|
||
|
|
||
|
|
@@ -253,7 +261,8 @@ def update_properties(self): | |
| self._lock_status = '{}{}'.format( | ||
| LOCK_ALARM_TYPE.get(str(alarm_type)), | ||
| 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( | ||
| LOCK_ALARM_TYPE.get(str(alarm_type)), str(alarm_level)) | ||
| return | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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, | ||
| }) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. continuation line missing indentation or outdented |
||
| 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, | ||
| }) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. continuation line missing indentation or outdented |
||
| 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, | ||
| }) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. continuation line missing indentation or outdented |
||
| 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, | ||
| }) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. continuation line missing indentation or outdented |
||
| yield from hass.async_block_till_done() | ||
|
|
||
| assert value1.data == '\0\0\0' | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
continuation line under-indented for visual indent