From d63028e44af753dd0df2e16273b0aa5e253fc859 Mon Sep 17 00:00:00 2001 From: John Arild Berentsen Date: Wed, 12 Apr 2017 19:09:29 +0200 Subject: [PATCH] Add communication data attributes to Zwave node_entity (#6813) * Add quality attribute to node * Move quality to node_entity * adjustments * Line lenght * flake8 * Cleanup and add each entry from getNodeStatistics as attribute * resolve conflict * Move NETWORK to hass.data * Test Done * Cleanup from review * Resolve conflicts * Review changes * Long lines.... * blank line * import-error-disable * Update tests Part1... Again * Hound * Argh! * Argh! * YABBADABBADOOOOOOO! * Enhance tests * hound * Resolve * resolve tests... --- homeassistant/components/cover/zwave.py | 13 +-- homeassistant/components/lock/zwave.py | 7 +- homeassistant/components/zwave/__init__.py | 61 ++++++------ homeassistant/components/zwave/node_entity.py | 17 +++- tests/components/cover/test_zwave.py | 52 ++++++---- tests/components/lock/test_zwave.py | 94 +++++++++---------- tests/components/zwave/test_init.py | 75 ++++++++------- tests/components/zwave/test_node_entity.py | 69 +++++++++++++- 8 files changed, 240 insertions(+), 148 deletions(-) diff --git a/homeassistant/components/cover/zwave.py b/homeassistant/components/cover/zwave.py index 3f26da183b5..f860e52de95 100644 --- a/homeassistant/components/cover/zwave.py +++ b/homeassistant/components/cover/zwave.py @@ -20,13 +20,13 @@ _LOGGER = logging.getLogger(__name__) SUPPORT_GARAGE = SUPPORT_OPEN | SUPPORT_CLOSE -def get_device(values, node_config, **kwargs): +def get_device(hass, values, node_config, **kwargs): """Create zwave entity device.""" invert_buttons = node_config.get(zwave.CONF_INVERT_OPENCLOSE_BUTTONS) if (values.primary.command_class == zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL and values.primary.index == 0): - return ZwaveRollershutter(values, invert_buttons) + return ZwaveRollershutter(hass, values, invert_buttons) elif (values.primary.command_class in [ zwave.const.COMMAND_CLASS_SWITCH_BINARY, zwave.const.COMMAND_CLASS_BARRIER_OPERATOR]): @@ -37,10 +37,11 @@ def get_device(values, node_config, **kwargs): class ZwaveRollershutter(zwave.ZWaveDeviceEntity, CoverDevice): """Representation of an Zwave roller shutter.""" - def __init__(self, values, invert_buttons): + def __init__(self, hass, values, invert_buttons): """Initialize the zwave rollershutter.""" ZWaveDeviceEntity.__init__(self, values, DOMAIN) # pylint: disable=no-member + self._network = hass.data[zwave.ZWAVE_NETWORK] self._open_id = None self._close_id = None self._current_position = None @@ -90,11 +91,11 @@ class ZwaveRollershutter(zwave.ZWaveDeviceEntity, CoverDevice): def open_cover(self, **kwargs): """Move the roller shutter up.""" - zwave.NETWORK.manager.pressButton(self._open_id) + self._network.manager.pressButton(self._open_id) def close_cover(self, **kwargs): """Move the roller shutter down.""" - zwave.NETWORK.manager.pressButton(self._close_id) + self._network.manager.pressButton(self._close_id) def set_cover_position(self, position, **kwargs): """Move the roller shutter to a specific position.""" @@ -102,7 +103,7 @@ class ZwaveRollershutter(zwave.ZWaveDeviceEntity, CoverDevice): def stop_cover(self, **kwargs): """Stop the roller shutter.""" - zwave.NETWORK.manager.releaseButton(self._open_id) + self._network.manager.releaseButton(self._open_id) class ZwaveGarageDoor(zwave.ZWaveDeviceEntity, CoverDevice): diff --git a/homeassistant/components/lock/zwave.py b/homeassistant/components/lock/zwave.py index 676775aace5..9a3e2e34fcc 100644 --- a/homeassistant/components/lock/zwave.py +++ b/homeassistant/components/lock/zwave.py @@ -128,11 +128,12 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): descriptions = load_yaml_config_file( path.join(path.dirname(__file__), 'services.yaml')) + network = hass.data[zwave.ZWAVE_NETWORK] def set_usercode(service): """Set the usercode to index X on the lock.""" node_id = service.data.get(zwave.const.ATTR_NODE_ID) - lock_node = zwave.NETWORK.nodes[node_id] + lock_node = network.nodes[node_id] code_slot = service.data.get(ATTR_CODE_SLOT) usercode = service.data.get(ATTR_USERCODE) @@ -151,7 +152,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): def get_usercode(service): """Get a usercode at index X on the lock.""" node_id = service.data.get(zwave.const.ATTR_NODE_ID) - lock_node = zwave.NETWORK.nodes[node_id] + lock_node = network.nodes[node_id] code_slot = service.data.get(ATTR_CODE_SLOT) for value in lock_node.get_values( @@ -164,7 +165,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): def clear_usercode(service): """Set usercode to slot X on the lock.""" node_id = service.data.get(zwave.const.ATTR_NODE_ID) - lock_node = zwave.NETWORK.nodes[node_id] + lock_node = network.nodes[node_id] code_slot = service.data.get(ATTR_CODE_SLOT) data = '' diff --git a/homeassistant/components/zwave/__init__.py b/homeassistant/components/zwave/__init__.py index 38827bc5533..eb755dbf1b8 100755 --- a/homeassistant/components/zwave/__init__.py +++ b/homeassistant/components/zwave/__init__.py @@ -65,8 +65,7 @@ DEFAULT_CONF_REFRESH_VALUE = False DEFAULT_CONF_REFRESH_DELAY = 5 DATA_ZWAVE_DICT = 'zwave_devices' - -NETWORK = None +ZWAVE_NETWORK = 'zwave_network' RENAME_NODE_SCHEMA = vol.Schema({ vol.Required(const.ATTR_NODE_ID): vol.Coerce(int), @@ -200,7 +199,7 @@ def get_config_value(node, value_index, tries=5): @asyncio.coroutine def async_setup_platform(hass, config, async_add_devices, discovery_info=None): """Generic Z-Wave platform setup.""" - if discovery_info is None or NETWORK is None: + if discovery_info is None or ZWAVE_NETWORK not in hass.data: return False device = hass.data[DATA_ZWAVE_DICT].pop( @@ -218,9 +217,6 @@ def setup(hass, config): Will automatically load components to support devices found on the network. """ - # pylint: disable=global-statement, import-error - global NETWORK - descriptions = conf_util.load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) @@ -232,6 +228,7 @@ def setup(hass, config): "https://home-assistant.io/components/zwave/") return False from pydispatch import dispatcher + # pylint: disable=import-error from openzwave.option import ZWaveOption from openzwave.network import ZWaveNetwork from openzwave.group import ZWaveGroup @@ -257,7 +254,7 @@ def setup(hass, config): options.set_console_output(use_debug) options.lock() - NETWORK = ZWaveNetwork(options, autostart=False) + network = hass.data[ZWAVE_NETWORK] = ZWaveNetwork(options, autostart=False) hass.data[DATA_ZWAVE_DICT] = {} if use_debug: # pragma: no cover @@ -301,7 +298,7 @@ def setup(hass, config): def node_added(node): """Called when a node is added on the network.""" - entity = ZWaveNodeEntity(node) + entity = ZWaveNodeEntity(node, network) node_config = device_config.get(entity.entity_id) if node_config.get(CONF_IGNORED): _LOGGER.info( @@ -354,49 +351,49 @@ def setup(hass, config): def add_node(service): """Switch into inclusion mode.""" _LOGGER.info("Zwave add_node have been initialized.") - NETWORK.controller.add_node() + network.controller.add_node() def add_node_secure(service): """Switch into secure inclusion mode.""" _LOGGER.info("Zwave add_node_secure have been initialized.") - NETWORK.controller.add_node(True) + network.controller.add_node(True) def remove_node(service): """Switch into exclusion mode.""" _LOGGER.info("Zwave remove_node have been initialized.") - NETWORK.controller.remove_node() + network.controller.remove_node() def cancel_command(service): """Cancel a running controller command.""" _LOGGER.info("Cancel running ZWave command.") - NETWORK.controller.cancel_command() + network.controller.cancel_command() def heal_network(service): """Heal the network.""" _LOGGER.info("ZWave heal running.") - NETWORK.heal() + network.heal() def soft_reset(service): """Soft reset the controller.""" _LOGGER.info("Zwave soft_reset have been initialized.") - NETWORK.controller.soft_reset() + network.controller.soft_reset() def test_network(service): """Test the network by sending commands to all the nodes.""" _LOGGER.info("Zwave test_network have been initialized.") - NETWORK.test() + network.test() def stop_network(_service_or_event): """Stop Z-Wave network.""" _LOGGER.info("Stopping ZWave network.") - NETWORK.stop() + network.stop() if hass.state == CoreState.running: hass.bus.fire(const.EVENT_NETWORK_STOP) def rename_node(service): """Rename a node.""" node_id = service.data.get(const.ATTR_NODE_ID) - node = NETWORK.nodes[node_id] + node = hass.data[ZWAVE_NETWORK].nodes[node_id] name = service.data.get(const.ATTR_NAME) node.name = name _LOGGER.info( @@ -406,18 +403,18 @@ def setup(hass, config): """Remove failed node.""" node_id = service.data.get(const.ATTR_NODE_ID) _LOGGER.info('Trying to remove zwave node %d', node_id) - NETWORK.controller.remove_failed_node(node_id) + network.controller.remove_failed_node(node_id) def replace_failed_node(service): """Replace failed node.""" node_id = service.data.get(const.ATTR_NODE_ID) _LOGGER.info('Trying to replace zwave node %d', node_id) - NETWORK.controller.replace_failed_node(node_id) + network.controller.replace_failed_node(node_id) def set_config_parameter(service): """Set a config parameter to a node.""" node_id = service.data.get(const.ATTR_NODE_ID) - node = NETWORK.nodes[node_id] + node = network.nodes[node_id] param = service.data.get(const.ATTR_CONFIG_PARAMETER) selection = service.data.get(const.ATTR_CONFIG_VALUE) size = service.data.get(const.ATTR_CONFIG_SIZE, 2) @@ -446,7 +443,7 @@ def setup(hass, config): def print_config_parameter(service): """Print a config parameter from a node.""" node_id = service.data.get(const.ATTR_NODE_ID) - node = NETWORK.nodes[node_id] + node = network.nodes[node_id] param = service.data.get(const.ATTR_CONFIG_PARAMETER) _LOGGER.info("Config parameter %s on Node %s : %s", param, node_id, get_config_value(node, param)) @@ -454,13 +451,13 @@ def setup(hass, config): def print_node(service): """Print all information about z-wave node.""" node_id = service.data.get(const.ATTR_NODE_ID) - node = NETWORK.nodes[node_id] + node = network.nodes[node_id] nice_print_node(node) def set_wakeup(service): """Set wake-up interval of a node.""" node_id = service.data.get(const.ATTR_NODE_ID) - node = NETWORK.nodes[node_id] + node = network.nodes[node_id] value = service.data.get(const.ATTR_CONFIG_VALUE) if node.can_wake_up(): for value_id in node.get_values( @@ -478,7 +475,7 @@ def setup(hass, config): group = service.data.get(const.ATTR_GROUP) instance = service.data.get(const.ATTR_INSTANCE) - node = ZWaveGroup(group, NETWORK, node_id) + node = ZWaveGroup(group, network, node_id) if association_type == 'add': node.add_association(target_node_id, instance) _LOGGER.info("Adding association for node:%s in group:%s " @@ -500,13 +497,13 @@ def setup(hass, config): def refresh_node(service): """Refresh all node info.""" node_id = service.data.get(const.ATTR_NODE_ID) - node = NETWORK.nodes[node_id] + node = network.nodes[node_id] node.refresh_info() def start_zwave(_service_or_event): """Startup Z-Wave network.""" _LOGGER.info("Starting ZWave network.") - NETWORK.start() + network.start() hass.bus.fire(const.EVENT_NETWORK_START) # Need to be in STATE_AWAKED before talking to nodes. @@ -514,8 +511,9 @@ def setup(hass, config): # to be ready. for i in range(const.NETWORK_READY_WAIT_SECS): _LOGGER.debug( - "network state: %d %s", NETWORK.state, NETWORK.state_str) - if NETWORK.state >= NETWORK.STATE_AWAKED: + "network state: %d %s", hass.data[ZWAVE_NETWORK].state, + network.state_str) + if network.state >= network.STATE_AWAKED: _LOGGER.info("zwave ready after %d seconds", i) break time.sleep(1) @@ -524,14 +522,15 @@ def setup(hass, config): "zwave not ready after %d seconds, continuing anyway", const.NETWORK_READY_WAIT_SECS) _LOGGER.info( - "final network state: %d %s", NETWORK.state, NETWORK.state_str) + "final network state: %d %s", network.state, + network.state_str) polling_interval = convert( config[DOMAIN].get(CONF_POLLING_INTERVAL), int) if polling_interval is not None: - NETWORK.set_poll_interval(polling_interval, False) + network.set_poll_interval(polling_interval, False) - poll_interval = NETWORK.get_poll_interval() + poll_interval = network.get_poll_interval() _LOGGER.info("zwave polling interval set to %d ms", poll_interval) hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_network) diff --git a/homeassistant/components/zwave/node_entity.py b/homeassistant/components/zwave/node_entity.py index 0ec699b8ee6..d0c75dd0b73 100644 --- a/homeassistant/components/zwave/node_entity.py +++ b/homeassistant/components/zwave/node_entity.py @@ -22,6 +22,10 @@ _REQUIRED_ATTRIBUTES = [ ATTR_QUERY_STAGE, ATTR_AWAKE, ATTR_READY, ATTR_FAILED, 'is_info_received', 'max_baud_rate', 'is_zwave_plus'] _OPTIONAL_ATTRIBUTES = ['capabilities', 'neighbors', 'location'] +_COMM_ATTRIBUTES = [ + 'sentCnt', 'sentFailed', 'retries', 'receivedCnt', 'receivedDups', + 'receivedUnsolicited', 'sentTS', 'receivedTS', 'lastRequestRTT', + 'averageRequestRTT', 'lastResponseRTT', 'averageResponseRTT'] ATTRIBUTES = _REQUIRED_ATTRIBUTES + _OPTIONAL_ATTRIBUTES @@ -65,12 +69,13 @@ def sub_status(status, stage): class ZWaveNodeEntity(ZWaveBaseEntity): """Representation of a Z-Wave node.""" - def __init__(self, node): + def __init__(self, node, network): """Initialize node.""" # pylint: disable=import-error super().__init__() from openzwave.network import ZWaveNetwork from pydispatch import dispatcher + self._network = network self.node = node self.node_id = self.node.node_id self._name = node_name(self.node) @@ -95,13 +100,22 @@ class ZWaveNodeEntity(ZWaveBaseEntity): return self.node_changed() + def get_node_statistics(self): + """Retrieve statistics from the node.""" + return self._network.manager.getNodeStatistics(self._network.home_id, + self.node_id) + def node_changed(self): """Update node properties.""" self._attributes = {} + stats = self.get_node_statistics() + for attr in ATTRIBUTES: value = getattr(self.node, attr) if attr in _REQUIRED_ATTRIBUTES or value: self._attributes[attr] = value + for attr in _COMM_ATTRIBUTES: + self._attributes[attr] = stats[attr] if self.node.can_wake_up(): for value in self.node.get_values(COMMAND_CLASS_WAKE_UP).values(): @@ -152,4 +166,5 @@ class ZWaveNodeEntity(ZWaveBaseEntity): attrs[ATTR_BATTERY_LEVEL] = self.battery_level if self.wakeup_interval is not None: attrs[ATTR_WAKEUP] = self.wakeup_interval + return attrs diff --git a/tests/components/cover/test_zwave.py b/tests/components/cover/test_zwave.py index 425331ff35c..aebc04c2d4c 100644 --- a/tests/components/cover/test_zwave.py +++ b/tests/components/cover/test_zwave.py @@ -1,5 +1,5 @@ """Test Z-Wave cover devices.""" -from unittest.mock import patch +from unittest.mock import MagicMock from homeassistant.components.cover import zwave, SUPPORT_OPEN, SUPPORT_CLOSE from homeassistant.components.zwave import const @@ -8,58 +8,66 @@ from tests.mock.zwave import ( MockNode, MockValue, MockEntityValues, value_changed) -def test_get_device_detects_none(mock_openzwave): +def test_get_device_detects_none(hass, mock_openzwave): """Test device returns none.""" node = MockNode() value = MockValue(data=0, node=node) values = MockEntityValues(primary=value, node=node) - device = zwave.get_device(node=node, values=values, node_config={}) + device = zwave.get_device(hass=hass, node=node, values=values, + node_config={}) assert device is None -def test_get_device_detects_rollershutter(mock_openzwave): +def test_get_device_detects_rollershutter(hass, mock_openzwave): """Test device returns rollershutter.""" + hass.data[zwave.zwave.ZWAVE_NETWORK] = MagicMock() node = MockNode() value = MockValue(data=0, node=node, command_class=const.COMMAND_CLASS_SWITCH_MULTILEVEL) values = MockEntityValues(primary=value, open=None, close=None, node=node) - device = zwave.get_device(node=node, values=values, node_config={}) + device = zwave.get_device(hass=hass, node=node, values=values, + node_config={}) assert isinstance(device, zwave.ZwaveRollershutter) -def test_get_device_detects_garagedoor(mock_openzwave): +def test_get_device_detects_garagedoor(hass, mock_openzwave): """Test device returns garage door.""" node = MockNode() value = MockValue(data=0, node=node, command_class=const.COMMAND_CLASS_BARRIER_OPERATOR) values = MockEntityValues(primary=value, node=node) - device = zwave.get_device(node=node, values=values, node_config={}) + device = zwave.get_device(hass=hass, node=node, values=values, + node_config={}) assert isinstance(device, zwave.ZwaveGarageDoor) assert device.device_class == "garage" assert device.supported_features == SUPPORT_OPEN | SUPPORT_CLOSE -def test_roller_no_position_workaround(mock_openzwave): +def test_roller_no_position_workaround(hass, mock_openzwave): """Test position changed.""" + hass.data[zwave.zwave.ZWAVE_NETWORK] = MagicMock() node = MockNode(manufacturer_id='0047', product_type='5a52') value = MockValue(data=45, node=node, command_class=const.COMMAND_CLASS_SWITCH_MULTILEVEL) values = MockEntityValues(primary=value, open=None, close=None, node=node) - device = zwave.get_device(node=node, values=values, node_config={}) + device = zwave.get_device(hass=hass, node=node, values=values, + node_config={}) assert device.current_cover_position is None -def test_roller_value_changed(mock_openzwave): +def test_roller_value_changed(hass, mock_openzwave): """Test position changed.""" + hass.data[zwave.zwave.ZWAVE_NETWORK] = MagicMock() node = MockNode() value = MockValue(data=None, node=node, command_class=const.COMMAND_CLASS_SWITCH_MULTILEVEL) values = MockEntityValues(primary=value, open=None, close=None, node=node) - device = zwave.get_device(node=node, values=values, node_config={}) + device = zwave.get_device(hass=hass, node=node, values=values, + node_config={}) assert device.current_cover_position is None assert device.is_closed is None @@ -83,9 +91,9 @@ def test_roller_value_changed(mock_openzwave): assert not device.is_closed -@patch('homeassistant.components.zwave.NETWORK') -def test_roller_commands(mock_network, mock_openzwave): +def test_roller_commands(hass, mock_openzwave): """Test position changed.""" + mock_network = hass.data[zwave.zwave.ZWAVE_NETWORK] = MagicMock() node = MockNode() value = MockValue(data=50, node=node, command_class=const.COMMAND_CLASS_SWITCH_MULTILEVEL) @@ -93,7 +101,8 @@ def test_roller_commands(mock_network, mock_openzwave): close_value = MockValue(data=False, node=node) values = MockEntityValues(primary=value, open=open_value, close=close_value, node=node) - device = zwave.get_device(node=node, values=values, node_config={}) + device = zwave.get_device(hass=hass, node=node, values=values, + node_config={}) device.set_cover_position(25) assert node.set_dimmer.called @@ -117,9 +126,9 @@ def test_roller_commands(mock_network, mock_openzwave): assert value_id == open_value.value_id -@patch('homeassistant.components.zwave.NETWORK') -def test_roller_reverse_open_close(mock_network, mock_openzwave): +def test_roller_reverse_open_close(hass, mock_openzwave): """Test position changed.""" + mock_network = hass.data[zwave.zwave.ZWAVE_NETWORK] = MagicMock() node = MockNode() value = MockValue(data=50, node=node, command_class=const.COMMAND_CLASS_SWITCH_MULTILEVEL) @@ -128,6 +137,7 @@ def test_roller_reverse_open_close(mock_network, mock_openzwave): values = MockEntityValues(primary=value, open=open_value, close=close_value, node=node) device = zwave.get_device( + hass=hass, node=node, values=values, node_config={zwave.zwave.CONF_INVERT_OPENCLOSE_BUTTONS: True}) @@ -148,13 +158,14 @@ def test_roller_reverse_open_close(mock_network, mock_openzwave): assert value_id == close_value.value_id -def test_garage_value_changed(mock_openzwave): +def test_garage_value_changed(hass, mock_openzwave): """Test position changed.""" node = MockNode() value = MockValue(data=False, node=node, command_class=const.COMMAND_CLASS_BARRIER_OPERATOR) values = MockEntityValues(primary=value, node=node) - device = zwave.get_device(node=node, values=values, node_config={}) + device = zwave.get_device(hass=hass, node=node, values=values, + node_config={}) assert device.is_closed @@ -164,13 +175,14 @@ def test_garage_value_changed(mock_openzwave): assert not device.is_closed -def test_garage_commands(mock_openzwave): +def test_garage_commands(hass, mock_openzwave): """Test position changed.""" node = MockNode() value = MockValue(data=False, node=node, command_class=const.COMMAND_CLASS_BARRIER_OPERATOR) values = MockEntityValues(primary=value, node=node) - device = zwave.get_device(node=node, values=values, node_config={}) + device = zwave.get_device(hass=hass, node=node, values=values, + node_config={}) assert value.data is False device.open_cover() diff --git a/tests/components/lock/test_zwave.py b/tests/components/lock/test_zwave.py index 33cc2e61483..9fb634f49e2 100644 --- a/tests/components/lock/test_zwave.py +++ b/tests/components/lock/test_zwave.py @@ -7,7 +7,7 @@ from homeassistant.components.lock import zwave from homeassistant.components.zwave import const from tests.mock.zwave import ( - MockNode, MockValue, MockEntityValues, value_changed) + MockNode, MockValue, MockEntityValues, value_changed) def test_get_device_detects_lock(mock_openzwave): @@ -171,6 +171,7 @@ def test_lock_alarm_level(mock_openzwave): @asyncio.coroutine def test_lock_set_usercode_service(hass, mock_openzwave): """Test the zwave lock set_usercode service.""" + mock_network = hass.data[zwave.zwave.ZWAVE_NETWORK] = MagicMock() node = MockNode(node_id=12) value0 = MockValue(data=None, node=node, index=0) value1 = MockValue(data=None, node=node, index=1) @@ -182,31 +183,29 @@ def test_lock_set_usercode_service(hass, mock_openzwave): 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() + 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() + 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' @@ -214,6 +213,7 @@ def test_lock_set_usercode_service(hass, mock_openzwave): @asyncio.coroutine def test_lock_get_usercode_service(hass, mock_openzwave): """Test the zwave lock get_usercode service.""" + mock_network = hass.data[zwave.zwave.ZWAVE_NETWORK] = MagicMock() node = MockNode(node_id=12) value0 = MockValue(data=None, node=node, index=0) value1 = MockValue(data='1234', node=node, index=1) @@ -225,27 +225,24 @@ def test_lock_get_usercode_service(hass, mock_openzwave): 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' + 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.""" + mock_network = hass.data[zwave.zwave.ZWAVE_NETWORK] = MagicMock() node = MockNode(node_id=12) value0 = MockValue(data=None, node=node, index=0) value1 = MockValue(data='123', node=node, index=1) @@ -257,15 +254,14 @@ def test_lock_clear_usercode_service(hass, mock_openzwave): 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() + 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' diff --git a/tests/components/zwave/test_init.py b/tests/components/zwave/test_init.py index 2e80e5b5331..91902f9c4a8 100644 --- a/tests/components/zwave/test_init.py +++ b/tests/components/zwave/test_init.py @@ -7,7 +7,7 @@ from homeassistant.const import ATTR_ENTITY_ID, EVENT_HOMEASSISTANT_START from homeassistant.components import zwave from homeassistant.components.binary_sensor.zwave import get_device from homeassistant.components.zwave import ( - const, CONFIG_SCHEMA, CONF_DEVICE_CONFIG_GLOB) + const, CONFIG_SCHEMA, CONF_DEVICE_CONFIG_GLOB, ZWAVE_NETWORK) from homeassistant.setup import setup_component import pytest @@ -70,10 +70,10 @@ def test_config_access_error(): @asyncio.coroutine -@patch.object(zwave, 'NETWORK') -def test_setup_platform(mock_network, hass, mock_openzwave): +def test_setup_platform(hass, mock_openzwave): """Test invalid device config.""" mock_device = MagicMock() + hass.data[ZWAVE_NETWORK] = MagicMock() hass.data[zwave.DATA_ZWAVE_DICT] = {456: mock_device} async_add_devices = MagicMock() @@ -104,7 +104,7 @@ def test_zwave_ready_wait(hass, mock_openzwave): with patch.object(zwave.time, 'sleep') as mock_sleep: with patch.object(zwave, '_LOGGER') as mock_logger: - zwave.NETWORK.state = MockNetwork.STATE_STARTED + hass.data[ZWAVE_NETWORK].state = MockNetwork.STATE_STARTED hass.bus.async_fire(EVENT_HOMEASSISTANT_START) yield from hass.async_block_till_done() @@ -473,12 +473,15 @@ class TestZWaveServices(unittest.TestCase): # Initialize zwave setup_component(self.hass, 'zwave', {'zwave': {}}) self.hass.block_till_done() - zwave.NETWORK.state = MockNetwork.STATE_READY + self.zwave_network = self.hass.data[ZWAVE_NETWORK] + self.zwave_network.state = MockNetwork.STATE_READY self.hass.bus.fire(EVENT_HOMEASSISTANT_START) self.hass.block_till_done() def tearDown(self): # pylint: disable=invalid-name """Stop everything that was started.""" + self.hass.services.call('zwave', 'stop_network', {}) + self.hass.block_till_done() self.hass.stop() def test_add_node(self): @@ -486,58 +489,62 @@ class TestZWaveServices(unittest.TestCase): self.hass.services.call('zwave', 'add_node', {}) self.hass.block_till_done() - assert zwave.NETWORK.controller.add_node.called - assert len(zwave.NETWORK.controller.add_node.mock_calls) == 1 - assert len(zwave.NETWORK.controller.add_node.mock_calls[0][1]) == 0 + assert self.zwave_network.controller.add_node.called + assert len(self.zwave_network.controller + .add_node.mock_calls) == 1 + assert len(self.zwave_network.controller + .add_node.mock_calls[0][1]) == 0 def test_add_node_secure(self): """Test zwave add_node_secure service.""" self.hass.services.call('zwave', 'add_node_secure', {}) self.hass.block_till_done() - assert zwave.NETWORK.controller.add_node.called - assert len(zwave.NETWORK.controller.add_node.mock_calls) == 1 - assert zwave.NETWORK.controller.add_node.mock_calls[0][1][0] is True + assert self.zwave_network.controller.add_node.called + assert len(self.zwave_network.controller.add_node.mock_calls) == 1 + assert (self.zwave_network.controller + .add_node.mock_calls[0][1][0] is True) def test_remove_node(self): """Test zwave remove_node service.""" self.hass.services.call('zwave', 'remove_node', {}) self.hass.block_till_done() - assert zwave.NETWORK.controller.remove_node.called - assert len(zwave.NETWORK.controller.remove_node.mock_calls) == 1 + assert self.zwave_network.controller.remove_node.called + assert len(self.zwave_network.controller.remove_node.mock_calls) == 1 def test_cancel_command(self): """Test zwave cancel_command service.""" self.hass.services.call('zwave', 'cancel_command', {}) self.hass.block_till_done() - assert zwave.NETWORK.controller.cancel_command.called - assert len(zwave.NETWORK.controller.cancel_command.mock_calls) == 1 + assert self.zwave_network.controller.cancel_command.called + assert len(self.zwave_network.controller + .cancel_command.mock_calls) == 1 def test_heal_network(self): """Test zwave heal_network service.""" self.hass.services.call('zwave', 'heal_network', {}) self.hass.block_till_done() - assert zwave.NETWORK.heal.called - assert len(zwave.NETWORK.heal.mock_calls) == 1 + assert self.zwave_network.heal.called + assert len(self.zwave_network.heal.mock_calls) == 1 def test_soft_reset(self): """Test zwave soft_reset service.""" self.hass.services.call('zwave', 'soft_reset', {}) self.hass.block_till_done() - assert zwave.NETWORK.controller.soft_reset.called - assert len(zwave.NETWORK.controller.soft_reset.mock_calls) == 1 + assert self.zwave_network.controller.soft_reset.called + assert len(self.zwave_network.controller.soft_reset.mock_calls) == 1 def test_test_network(self): """Test zwave test_network service.""" self.hass.services.call('zwave', 'test_network', {}) self.hass.block_till_done() - assert zwave.NETWORK.test.called - assert len(zwave.NETWORK.test.mock_calls) == 1 + assert self.zwave_network.test.called + assert len(self.zwave_network.test.mock_calls) == 1 def test_stop_network(self): """Test zwave stop_network service.""" @@ -545,22 +552,22 @@ class TestZWaveServices(unittest.TestCase): self.hass.services.call('zwave', 'stop_network', {}) self.hass.block_till_done() - assert zwave.NETWORK.stop.called - assert len(zwave.NETWORK.stop.mock_calls) == 1 + assert self.zwave_network.stop.called + assert len(self.zwave_network.stop.mock_calls) == 1 assert mock_fire.called assert len(mock_fire.mock_calls) == 2 assert mock_fire.mock_calls[0][1][0] == const.EVENT_NETWORK_STOP def test_rename_node(self): """Test zwave rename_node service.""" - zwave.NETWORK.nodes = {11: MagicMock()} + self.zwave_network.nodes = {11: MagicMock()} self.hass.services.call('zwave', 'rename_node', { const.ATTR_NODE_ID: 11, const.ATTR_NAME: 'test_name', }) self.hass.block_till_done() - assert zwave.NETWORK.nodes[11].name == 'test_name' + assert self.zwave_network.nodes[11].name == 'test_name' def test_remove_failed_node(self): """Test zwave remove_failed_node service.""" @@ -569,7 +576,7 @@ class TestZWaveServices(unittest.TestCase): }) self.hass.block_till_done() - remove_failed_node = zwave.NETWORK.controller.remove_failed_node + remove_failed_node = self.zwave_network.controller.remove_failed_node assert remove_failed_node.called assert len(remove_failed_node.mock_calls) == 1 assert remove_failed_node.mock_calls[0][1][0] == 12 @@ -581,7 +588,7 @@ class TestZWaveServices(unittest.TestCase): }) self.hass.block_till_done() - replace_failed_node = zwave.NETWORK.controller.replace_failed_node + replace_failed_node = self.zwave_network.controller.replace_failed_node assert replace_failed_node.called assert len(replace_failed_node.mock_calls) == 1 assert replace_failed_node.mock_calls[0][1][0] == 13 @@ -600,7 +607,7 @@ class TestZWaveServices(unittest.TestCase): ) node = MockNode(node_id=14) node.get_values.return_value = {12: value, 13: value_list} - zwave.NETWORK.nodes = {14: node} + self.zwave_network.nodes = {14: node} self.hass.services.call('zwave', 'set_config_parameter', { const.ATTR_NODE_ID: 14, @@ -655,7 +662,7 @@ class TestZWaveServices(unittest.TestCase): ) node = MockNode(node_id=14) node.values = {12: value1, 13: value2} - zwave.NETWORK.nodes = {14: node} + self.zwave_network.nodes = {14: node} with patch.object(zwave, '_LOGGER') as mock_logger: self.hass.services.call('zwave', 'print_config_parameter', { @@ -674,7 +681,7 @@ class TestZWaveServices(unittest.TestCase): """Test zwave print_config_parameter service.""" node1 = MockNode(node_id=14) node2 = MockNode(node_id=15) - zwave.NETWORK.nodes = {14: node1, 15: node2} + self.zwave_network.nodes = {14: node1, 15: node2} with patch.object(zwave, 'pprint') as mock_pprint: self.hass.services.call('zwave', 'print_node', { @@ -695,7 +702,7 @@ class TestZWaveServices(unittest.TestCase): node = MockNode(node_id=14) node.values = {12: value} node.get_values.return_value = node.values - zwave.NETWORK.nodes = {14: node} + self.zwave_network.nodes = {14: node} self.hass.services.call('zwave', 'set_wakeup', { const.ATTR_NODE_ID: 14, @@ -727,7 +734,7 @@ class TestZWaveServices(unittest.TestCase): node = MockNode(node_id=14) node.values = {12: value} node.get_values.return_value = node.values - zwave.NETWORK.nodes = {14: node} + self.zwave_network.nodes = {14: node} self.hass.services.call('zwave', 'change_association', { const.ATTR_ASSOCIATION: 'add', @@ -760,7 +767,7 @@ class TestZWaveServices(unittest.TestCase): node = MockNode(node_id=14) node.values = {12: value} node.get_values.return_value = node.values - zwave.NETWORK.nodes = {14: node} + self.zwave_network.nodes = {14: node} self.hass.services.call('zwave', 'change_association', { const.ATTR_ASSOCIATION: 'remove', @@ -808,7 +815,7 @@ class TestZWaveServices(unittest.TestCase): def test_refresh_node(self): """Test zwave refresh_node service.""" node = MockNode(node_id=14) - zwave.NETWORK.nodes = {14: node} + self.zwave_network.nodes = {14: node} self.hass.services.call('zwave', 'refresh_node', { const.ATTR_NODE_ID: 14, }) diff --git a/tests/components/zwave/test_node_entity.py b/tests/components/zwave/test_node_entity.py index c171155404f..223f8f8c419 100644 --- a/tests/components/zwave/test_node_entity.py +++ b/tests/components/zwave/test_node_entity.py @@ -1,7 +1,7 @@ """Test Z-Wave node entity.""" import asyncio import unittest -from unittest.mock import patch +from unittest.mock import patch, MagicMock import tests.mock.zwave as mock_zwave import pytest from homeassistant.components.zwave import node_entity @@ -36,11 +36,13 @@ class TestZWaveNodeEntity(unittest.TestCase): def setUp(self): """Initialize values for this testcase class.""" + self.zwave_network = MagicMock() self.node = mock_zwave.MockNode( query_stage='Dynamic', is_awake=True, is_ready=False, is_failed=False, is_info_received=True, max_baud_rate=40000, is_zwave_plus=False, capabilities=[], neighbors=[], location=None) - self.entity = node_entity.ZWaveNodeEntity(self.node) + self.entity = node_entity.ZWaveNodeEntity(self.node, + self.zwave_network) def test_network_node_changed_from_value(self): """Test for network_node_changed.""" @@ -76,14 +78,61 @@ class TestZWaveNodeEntity(unittest.TestCase): def test_node_changed(self): """Test node_changed function.""" + self.maxDiff = None self.assertEqual({'node_id': self.node.node_id}, self.entity.device_state_attributes) self.node.get_values.return_value = { 1: mock_zwave.MockValue(data=1800) } + self.zwave_network.manager.getNodeStatistics.return_value = { + "receivedCnt": 4, "ccData": [{"receivedCnt": 0, + "commandClassId": 134, + "sentCnt": 0}, + {"receivedCnt": 1, + "commandClassId": 133, + "sentCnt": 1}, + {"receivedCnt": 1, + "commandClassId": 115, + "sentCnt": 1}, + {"receivedCnt": 0, + "commandClassId": 114, + "sentCnt": 0}, + {"receivedCnt": 0, + "commandClassId": 112, + "sentCnt": 0}, + {"receivedCnt": 1, + "commandClassId": 32, + "sentCnt": 1}, + {"receivedCnt": 0, + "commandClassId": 0, + "sentCnt": 0}], + "receivedUnsolicited": 0, + "sentTS": "2017-03-27 15:38:15:620 ", "averageRequestRTT": 2462, + "lastResponseRTT": 3679, "retries": 0, "sentFailed": 1, + "sentCnt": 7, "quality": 0, "lastRequestRTT": 1591, + "lastReceivedMessage": [0, 4, 0, 15, 3, 32, 3, 0, 221, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0], "receivedDups": 1, + "averageResponseRTT": 2443, + "receivedTS": "2017-03-27 15:38:19:298 "} self.entity.node_changed() - self.assertEqual( {'node_id': self.node.node_id, 'query_stage': 'Dynamic', @@ -94,7 +143,19 @@ class TestZWaveNodeEntity(unittest.TestCase): 'max_baud_rate': 40000, 'is_zwave_plus': False, 'battery_level': 42, - 'wake_up_interval': 1800}, + 'wake_up_interval': 1800, + 'averageRequestRTT': 2462, + 'averageResponseRTT': 2443, + 'lastRequestRTT': 1591, + 'lastResponseRTT': 3679, + 'receivedCnt': 4, + 'receivedDups': 1, + 'receivedTS': '2017-03-27 15:38:19:298 ', + 'receivedUnsolicited': 0, + 'retries': 0, + 'sentCnt': 7, + 'sentFailed': 1, + 'sentTS': '2017-03-27 15:38:15:620 '}, self.entity.device_state_attributes) self.node.can_wake_up_value = False