diff --git a/homeassistant/components/zwave/node_entity.py b/homeassistant/components/zwave/node_entity.py index 66c3452f7c8..44241e91daf 100644 --- a/homeassistant/components/zwave/node_entity.py +++ b/homeassistant/components/zwave/node_entity.py @@ -17,6 +17,7 @@ from .const import ( EVENT_NODE_EVENT, EVENT_SCENE_ACTIVATED, COMMAND_CLASS_CENTRAL_SCENE, + COMMAND_CLASS_VERSION, DOMAIN, ) from .util import node_name, is_node_parsed, node_device_id_and_name @@ -30,6 +31,7 @@ ATTR_FAILED = "is_failed" ATTR_PRODUCT_NAME = "product_name" ATTR_MANUFACTURER_NAME = "manufacturer_name" ATTR_NODE_NAME = "node_name" +ATTR_APPLICATION_VERSION = "application_version" STAGE_COMPLETE = "Complete" @@ -130,10 +132,14 @@ class ZWaveNodeEntity(ZWaveBaseEntity): self._product_name = node.product_name self._manufacturer_name = node.manufacturer_name self._unique_id = self._compute_unique_id() + self._application_version = None self._attributes = {} self.wakeup_interval = None self.location = None self.battery_level = None + dispatcher.connect( + self.network_node_value_added, ZWaveNetwork.SIGNAL_VALUE_ADDED + ) dispatcher.connect(self.network_node_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) dispatcher.connect(self.network_node_changed, ZWaveNetwork.SIGNAL_NODE) dispatcher.connect(self.network_node_changed, ZWaveNetwork.SIGNAL_NOTIFICATION) @@ -161,6 +167,24 @@ class ZWaveNodeEntity(ZWaveBaseEntity): info["via_device"] = (DOMAIN, 1) return info + def maybe_update_application_version(self, value): + """Update application version if value is a Command Class Version, Application Value.""" + if ( + value + and value.command_class == COMMAND_CLASS_VERSION + and value.label == "Application Version" + ): + self._application_version = value.data + + def network_node_value_added(self, node=None, value=None, args=None): + """Handle a added value to a none on the network.""" + if node and node.node_id != self.node_id: + return + if args is not None and "nodeId" in args and args["nodeId"] != self.node_id: + return + + self.maybe_update_application_version(value) + def network_node_changed(self, node=None, value=None, args=None): """Handle a changed node on the network.""" if node and node.node_id != self.node_id: @@ -172,6 +196,8 @@ class ZWaveNodeEntity(ZWaveBaseEntity): if value is not None and value.command_class == COMMAND_CLASS_CENTRAL_SCENE: self.central_scene_activated(value.index, value.data) + self.maybe_update_application_version(value) + self.node_changed() def get_node_statistics(self): @@ -343,6 +369,8 @@ class ZWaveNodeEntity(ZWaveBaseEntity): attrs[ATTR_BATTERY_LEVEL] = self.battery_level if self.wakeup_interval is not None: attrs[ATTR_WAKEUP] = self.wakeup_interval + if self._application_version is not None: + attrs[ATTR_APPLICATION_VERSION] = self._application_version return attrs diff --git a/tests/components/zwave/test_node_entity.py b/tests/components/zwave/test_node_entity.py index 1e5ec615088..dba187d7b96 100644 --- a/tests/components/zwave/test_node_entity.py +++ b/tests/components/zwave/test_node_entity.py @@ -164,6 +164,73 @@ async def test_central_scene_activated(hass, mock_openzwave): assert events[0].data[const.ATTR_SCENE_DATA] == scene_data +async def test_application_version(hass, mock_openzwave): + """Test application version.""" + mock_receivers = {} + + signal_mocks = [ + mock_zwave.MockNetwork.SIGNAL_VALUE_CHANGED, + mock_zwave.MockNetwork.SIGNAL_VALUE_ADDED, + ] + + def mock_connect(receiver, signal, *args, **kwargs): + if signal in signal_mocks: + mock_receivers[signal] = receiver + + node = mock_zwave.MockNode(node_id=11) + + with patch("pydispatch.dispatcher.connect", new=mock_connect): + entity = node_entity.ZWaveNodeEntity(node, mock_openzwave) + + for signal_mock in signal_mocks: + assert signal_mock in mock_receivers.keys() + + events = [] + + def listener(event): + events.append(event) + + # Make sure application version isn't set before + assert ( + node_entity.ATTR_APPLICATION_VERSION + not in entity.device_state_attributes.keys() + ) + + # Add entity to hass + entity.hass = hass + entity.entity_id = "zwave.mock_node" + + # Fire off an added value + value = mock_zwave.MockValue( + command_class=const.COMMAND_CLASS_VERSION, + label="Application Version", + data="5.10", + ) + hass.async_add_job( + mock_receivers[mock_zwave.MockNetwork.SIGNAL_VALUE_ADDED], node, value + ) + await hass.async_block_till_done() + + assert ( + entity.device_state_attributes[node_entity.ATTR_APPLICATION_VERSION] == "5.10" + ) + + # Fire off a changed + value = mock_zwave.MockValue( + command_class=const.COMMAND_CLASS_VERSION, + label="Application Version", + data="4.14", + ) + hass.async_add_job( + mock_receivers[mock_zwave.MockNetwork.SIGNAL_VALUE_CHANGED], node, value + ) + await hass.async_block_till_done() + + assert ( + entity.device_state_attributes[node_entity.ATTR_APPLICATION_VERSION] == "4.14" + ) + + @pytest.mark.usefixtures("mock_openzwave") class TestZWaveNodeEntity(unittest.TestCase): """Class to test ZWaveNodeEntity."""