diff --git a/homeassistant/components/light/zwave.py b/homeassistant/components/light/zwave.py index 9488ad38a59..752928f71a4 100644 --- a/homeassistant/components/light/zwave.py +++ b/homeassistant/components/light/zwave.py @@ -47,11 +47,11 @@ TEMP_COLD_HASS = (TEMP_COLOR_MAX - TEMP_COLOR_MIN) / 3 + TEMP_COLOR_MIN def get_device(node, values, node_config, **kwargs): """Create Z-Wave entity device.""" - name = '{}.{}'.format(DOMAIN, zwave.object_id(values.primary)) refresh = node_config.get(zwave.CONF_REFRESH_VALUE) delay = node_config.get(zwave.CONF_REFRESH_DELAY) - _LOGGER.debug("name=%s node_config=%s CONF_REFRESH_VALUE=%s" - " CONF_REFRESH_DELAY=%s", name, node_config, refresh, delay) + _LOGGER.debug("node=%d value=%d node_config=%s CONF_REFRESH_VALUE=%s" + " CONF_REFRESH_DELAY=%s", node.node_id, + values.primary.value_id, node_config, refresh, delay) if node.has_command_class(zwave.const.COMMAND_CLASS_SWITCH_COLOR): return ZwaveColorLight(values, refresh, delay) diff --git a/homeassistant/components/zwave/__init__.py b/homeassistant/components/zwave/__init__.py index e82ce286e01..a79a986dc7d 100755 --- a/homeassistant/components/zwave/__init__.py +++ b/homeassistant/components/zwave/__init__.py @@ -16,6 +16,7 @@ import voluptuous as vol from homeassistant.core import CoreState from homeassistant.loader import get_platform from homeassistant.helpers import discovery +from homeassistant.helpers.entity import generate_entity_id from homeassistant.helpers.entity_component import EntityComponent from homeassistant.const import ( ATTR_ENTITY_ID, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) @@ -55,6 +56,7 @@ CONF_DEVICE_CONFIG = 'device_config' CONF_DEVICE_CONFIG_GLOB = 'device_config_glob' CONF_DEVICE_CONFIG_DOMAIN = 'device_config_domain' CONF_NETWORK_KEY = 'network_key' +CONF_NEW_ENTITY_IDS = 'new_entity_ids' ATTR_POWER = 'power_consumption' @@ -149,6 +151,7 @@ CONFIG_SCHEMA = vol.Schema({ cv.positive_int, vol.Optional(CONF_USB_STICK_PATH, default=DEFAULT_CONF_USB_STICK_PATH): cv.string, + vol.Optional(CONF_NEW_ENTITY_IDS, default=False): cv.boolean, }), }, extra=vol.ALLOW_EXTRA) @@ -162,7 +165,7 @@ def _obj_to_dict(obj): def _value_name(value): """Return the name of the value.""" - return '{} {}'.format(node_name(value.node), value.label) + return '{} {}'.format(node_name(value.node), value.label).strip() def _node_object_id(node): @@ -250,6 +253,13 @@ def setup(hass, config): config[DOMAIN][CONF_DEVICE_CONFIG], config[DOMAIN][CONF_DEVICE_CONFIG_DOMAIN], config[DOMAIN][CONF_DEVICE_CONFIG_GLOB]) + new_entity_ids = config[DOMAIN][CONF_NEW_ENTITY_IDS] + if not new_entity_ids: + _LOGGER.warning( + "ZWave entity_ids will soon be changing. To opt in to new " + "entity_ids now, set `new_entity_ids: true` under zwave in your " + "configuration.yaml. See the following blog post for details: " + "https://home-assistant.io/blog/2017/06/15/zwave-entity-ids/") # Setup options options = ZWaveOption( @@ -311,30 +321,20 @@ def setup(hass, config): def node_added(node): """Handle a new node on the network.""" - entity = ZWaveNodeEntity(node, network) - node_config = device_config.get(entity.entity_id) + entity = ZWaveNodeEntity(node, network, new_entity_ids) + name = node_name(node) + if new_entity_ids: + generated_id = generate_entity_id(DOMAIN + '.{}', name, []) + else: + generated_id = entity.entity_id + node_config = device_config.get(generated_id) if node_config.get(CONF_IGNORED): _LOGGER.info( "Ignoring node entity %s due to device settings", - entity.entity_id) + generated_id) return component.add_entities([entity]) - def scene_activated(node, scene_id): - """Handle an activated scene on any node in the network.""" - hass.bus.fire(const.EVENT_SCENE_ACTIVATED, { - ATTR_ENTITY_ID: _node_object_id(node), - const.ATTR_OBJECT_ID: _node_object_id(node), - const.ATTR_SCENE_ID: scene_id - }) - - def node_event_activated(node, value): - """Handle a nodeevent on any node in the network.""" - hass.bus.fire(const.EVENT_NODE_EVENT, { - const.ATTR_OBJECT_ID: _node_object_id(node), - const.ATTR_BASIC_LEVEL: value - }) - def network_ready(): """Handle the query of all awake nodes.""" _LOGGER.info("Zwave network is ready for use. All awake nodes " @@ -352,10 +352,6 @@ def setup(hass, config): value_added, ZWaveNetwork.SIGNAL_VALUE_ADDED, weak=False) dispatcher.connect( node_added, ZWaveNetwork.SIGNAL_NODE_ADDED, weak=False) - dispatcher.connect( - scene_activated, ZWaveNetwork.SIGNAL_SCENE_EVENT, weak=False) - dispatcher.connect( - node_event_activated, ZWaveNetwork.SIGNAL_NODE_EVENT, weak=False) dispatcher.connect( network_ready, ZWaveNetwork.SIGNAL_AWAKE_NODES_QUERIED, weak=False) dispatcher.connect( @@ -757,9 +753,8 @@ class ZWaveDeviceEntityValues(): self.primary) if workaround_component and workaround_component != component: if workaround_component == workaround.WORKAROUND_IGNORE: - _LOGGER.info("Ignoring device %s due to workaround.", - "{}.{}".format( - component, object_id(self.primary))) + _LOGGER.info("Ignoring Node %d Value %d due to workaround.", + self.primary.node.node_id, self.primary.value_id) # No entity will be created for this value self._workaround_ignore = True return @@ -767,8 +762,13 @@ class ZWaveDeviceEntityValues(): workaround_component, component) component = workaround_component - name = "{}.{}".format(component, object_id(self.primary)) - node_config = self._device_config.get(name) + value_name = _value_name(self.primary) + if self._zwave_config[DOMAIN][CONF_NEW_ENTITY_IDS]: + generated_id = generate_entity_id( + component + '.{}', value_name, []) + else: + generated_id = "{}.{}".format(component, object_id(self.primary)) + node_config = self._device_config.get(generated_id) # Configure node _LOGGER.debug("Adding Node_id=%s Generic_command_class=%s, " @@ -781,7 +781,7 @@ class ZWaveDeviceEntityValues(): if node_config.get(CONF_IGNORED): _LOGGER.info( - "Ignoring entity %s due to device settings", name) + "Ignoring entity %s due to device settings", generated_id) # No entity will be created for this value self._workaround_ignore = True return @@ -802,6 +802,12 @@ class ZWaveDeviceEntityValues(): self._workaround_ignore = True return + device.old_entity_id = "{}.{}".format( + component, object_id(self.primary)) + device.new_entity_id = "{}.{}".format(component, slugify(device.name)) + if not self._zwave_config[DOMAIN][CONF_NEW_ENTITY_IDS]: + device.entity_id = device.old_entity_id + self._entity = device dict_id = id(self) @@ -828,7 +834,6 @@ class ZWaveDeviceEntity(ZWaveBaseEntity): self.values = values self.node = values.primary.node self.values.primary.set_change_verified(False) - self.entity_id = "{}.{}".format(domain, object_id(values.primary)) self._name = _value_name(self.values.primary) self._unique_id = "ZWAVE-{}-{}".format(self.node.node_id, @@ -895,6 +900,10 @@ class ZWaveDeviceEntity(ZWaveBaseEntity): """Return the device specific state attributes.""" attrs = { const.ATTR_NODE_ID: self.node_id, + const.ATTR_VALUE_INDEX: self.values.primary.index, + const.ATTR_VALUE_INSTANCE: self.values.primary.instance, + 'old_entity_id': self.old_entity_id, + 'new_entity_id': self.new_entity_id, } if self.power_consumption is not None: diff --git a/homeassistant/components/zwave/api.py b/homeassistant/components/zwave/api.py index 85e7b9c0f8f..181ab4ae18c 100644 --- a/homeassistant/components/zwave/api.py +++ b/homeassistant/components/zwave/api.py @@ -31,6 +31,8 @@ class ZWaveNodeValueView(HomeAssistantView): values_data[entity_values.primary.value_id] = { 'label': entity_values.primary.label, + 'index': entity_values.primary.index, + 'instance': entity_values.primary.instance, } return self.json(values_data) diff --git a/homeassistant/components/zwave/const.py b/homeassistant/components/zwave/const.py index 57e77fe49cb..4b18ef46475 100644 --- a/homeassistant/components/zwave/const.py +++ b/homeassistant/components/zwave/const.py @@ -14,6 +14,8 @@ ATTR_BASIC_LEVEL = "basic_level" ATTR_CONFIG_PARAMETER = "parameter" ATTR_CONFIG_SIZE = "size" ATTR_CONFIG_VALUE = "value" +ATTR_VALUE_INDEX = "value_index" +ATTR_VALUE_INSTANCE = "value_instance" NETWORK_READY_WAIT_SECS = 30 DISCOVERY_DEVICE = 'device' diff --git a/homeassistant/components/zwave/node_entity.py b/homeassistant/components/zwave/node_entity.py index 5a441114f55..3a810d00d2d 100644 --- a/homeassistant/components/zwave/node_entity.py +++ b/homeassistant/components/zwave/node_entity.py @@ -2,11 +2,13 @@ import logging from homeassistant.core import callback -from homeassistant.const import ATTR_BATTERY_LEVEL, ATTR_WAKEUP +from homeassistant.const import ATTR_BATTERY_LEVEL, ATTR_WAKEUP, ATTR_ENTITY_ID from homeassistant.helpers.entity import Entity from homeassistant.util import slugify -from .const import ATTR_NODE_ID, DOMAIN, COMMAND_CLASS_WAKE_UP +from .const import ( + ATTR_NODE_ID, COMMAND_CLASS_WAKE_UP, ATTR_SCENE_ID, ATTR_BASIC_LEVEL, + EVENT_NODE_EVENT, EVENT_SCENE_ACTIVATED, DOMAIN) from .util import node_name _LOGGER = logging.getLogger(__name__) @@ -38,6 +40,8 @@ class ZWaveBaseEntity(Entity): def __init__(self): """Initialize the base Z-Wave class.""" self._update_scheduled = False + self.old_entity_id = None + self.new_entity_id = None def maybe_schedule_update(self): """Maybe schedule state update. @@ -72,7 +76,7 @@ def sub_status(status, stage): class ZWaveNodeEntity(ZWaveBaseEntity): """Representation of a Z-Wave node.""" - def __init__(self, node, network): + def __init__(self, node, network, new_entity_ids): """Initialize node.""" # pylint: disable=import-error super().__init__() @@ -84,8 +88,11 @@ class ZWaveNodeEntity(ZWaveBaseEntity): self._name = node_name(self.node) self._product_name = node.product_name self._manufacturer_name = node.manufacturer_name - self.entity_id = "{}.{}_{}".format( + self.old_entity_id = "{}.{}_{}".format( DOMAIN, slugify(self._name), self.node_id) + self.new_entity_id = "{}.{}".format(DOMAIN, slugify(self._name)) + if not new_entity_ids: + self.entity_id = self.old_entity_id self._attributes = {} self.wakeup_interval = None self.location = None @@ -95,6 +102,10 @@ class ZWaveNodeEntity(ZWaveBaseEntity): dispatcher.connect(self.network_node_changed, ZWaveNetwork.SIGNAL_NODE) dispatcher.connect( self.network_node_changed, ZWaveNetwork.SIGNAL_NOTIFICATION) + dispatcher.connect( + self.network_node_event, ZWaveNetwork.SIGNAL_NODE_EVENT) + dispatcher.connect( + self.network_scene_activated, ZWaveNetwork.SIGNAL_SCENE_EVENT) def network_node_changed(self, node=None, args=None): """Handle a changed node on the network.""" @@ -134,6 +145,38 @@ class ZWaveNodeEntity(ZWaveBaseEntity): self.maybe_schedule_update() + def network_node_event(self, node, value): + """Handle a node activated event on the network.""" + if node.node_id == self.node.node_id: + self.node_event(value) + + def node_event(self, value): + """Handle a node activated event for this node.""" + if self.hass is None: + return + + self.hass.bus.fire(EVENT_NODE_EVENT, { + ATTR_ENTITY_ID: self.entity_id, + ATTR_NODE_ID: self.node.node_id, + ATTR_BASIC_LEVEL: value + }) + + def network_scene_activated(self, node, scene_id): + """Handle a scene activated event on the network.""" + if node.node_id == self.node.node_id: + self.scene_activated(scene_id) + + def scene_activated(self, scene_id): + """Handle an activated scene for this node.""" + if self.hass is None: + return + + self.hass.bus.fire(EVENT_SCENE_ACTIVATED, { + ATTR_ENTITY_ID: self.entity_id, + ATTR_NODE_ID: self.node.node_id, + ATTR_SCENE_ID: scene_id + }) + @property def state(self): """Return the state.""" @@ -169,6 +212,8 @@ class ZWaveNodeEntity(ZWaveBaseEntity): ATTR_NODE_NAME: self._name, ATTR_MANUFACTURER_NAME: self._manufacturer_name, ATTR_PRODUCT_NAME: self._product_name, + 'old_entity_id': self.old_entity_id, + 'new_entity_id': self.new_entity_id, } attrs.update(self._attributes) if self.battery_level is not None: diff --git a/tests/components/zwave/test_api.py b/tests/components/zwave/test_api.py index cf597f4104c..5fae8b0f317 100644 --- a/tests/components/zwave/test_api.py +++ b/tests/components/zwave/test_api.py @@ -16,7 +16,8 @@ def test_get_values(hass, test_client): ZWaveNodeValueView().register(app.router) node = MockNode(node_id=1) - value = MockValue(value_id=123456, node=node, label='Test Label') + value = MockValue(value_id=123456, node=node, label='Test Label', + instance=1, index=2) values = MockEntityValues(primary=value) node2 = MockNode(node_id=2) value2 = MockValue(value_id=234567, node=node2, label='Test Label 2') @@ -33,6 +34,8 @@ def test_get_values(hass, test_client): assert result == { '123456': { 'label': 'Test Label', + 'instance': 1, + 'index': 2, } } diff --git a/tests/components/zwave/test_init.py b/tests/components/zwave/test_init.py index eac33168fb7..0baa299c27c 100644 --- a/tests/components/zwave/test_init.py +++ b/tests/components/zwave/test_init.py @@ -214,7 +214,9 @@ def test_node_discovery(hass, mock_openzwave): mock_receivers.append(receiver) with patch('pydispatch.dispatcher.connect', new=mock_connect): - yield from async_setup_component(hass, 'zwave', {'zwave': {}}) + yield from async_setup_component(hass, 'zwave', {'zwave': { + 'new_entity_ids': True, + }}) assert len(mock_receivers) == 1 @@ -222,7 +224,7 @@ def test_node_discovery(hass, mock_openzwave): hass.async_add_job(mock_receivers[0], node) yield from hass.async_block_till_done() - assert hass.states.get('zwave.mock_node_14').state is 'unknown' + assert hass.states.get('zwave.mock_node').state is 'unknown' @asyncio.coroutine @@ -236,8 +238,9 @@ def test_node_ignored(hass, mock_openzwave): with patch('pydispatch.dispatcher.connect', new=mock_connect): yield from async_setup_component(hass, 'zwave', {'zwave': { + 'new_entity_ids': True, 'device_config': { - 'zwave.mock_node_14': { + 'zwave.mock_node': { 'ignored': True, }}}}) @@ -247,7 +250,7 @@ def test_node_ignored(hass, mock_openzwave): hass.async_add_job(mock_receivers[0], node) yield from hass.async_block_till_done() - assert hass.states.get('zwave.mock_node_14') is None + assert hass.states.get('zwave.mock_node') is None @asyncio.coroutine @@ -260,7 +263,9 @@ def test_value_discovery(hass, mock_openzwave): mock_receivers.append(receiver) with patch('pydispatch.dispatcher.connect', new=mock_connect): - yield from async_setup_component(hass, 'zwave', {'zwave': {}}) + yield from async_setup_component(hass, 'zwave', {'zwave': { + 'new_entity_ids': True, + }}) assert len(mock_receivers) == 1 @@ -272,7 +277,7 @@ def test_value_discovery(hass, mock_openzwave): yield from hass.async_block_till_done() assert hass.states.get( - 'binary_sensor.mock_node_mock_value_11_12_13').state is 'off' + 'binary_sensor.mock_node_mock_value').state is 'off' @asyncio.coroutine @@ -285,7 +290,9 @@ def test_value_discovery_existing_entity(hass, mock_openzwave): mock_receivers.append(receiver) with patch('pydispatch.dispatcher.connect', new=mock_connect): - yield from async_setup_component(hass, 'zwave', {'zwave': {}}) + yield from async_setup_component(hass, 'zwave', {'zwave': { + 'new_entity_ids': True, + }}) assert len(mock_receivers) == 1 @@ -297,9 +304,9 @@ def test_value_discovery_existing_entity(hass, mock_openzwave): hass.async_add_job(mock_receivers[0], node, setpoint) yield from hass.async_block_till_done() - assert hass.states.get('climate.mock_node_mock_value_11_12_13').attributes[ + assert hass.states.get('climate.mock_node_mock_value').attributes[ 'temperature'] == 22.0 - assert hass.states.get('climate.mock_node_mock_value_11_12_13').attributes[ + assert hass.states.get('climate.mock_node_mock_value').attributes[ 'current_temperature'] is None def mock_update(self): @@ -314,75 +321,12 @@ def test_value_discovery_existing_entity(hass, mock_openzwave): hass.async_add_job(mock_receivers[0], node, temperature) yield from hass.async_block_till_done() - assert hass.states.get('climate.mock_node_mock_value_11_12_13').attributes[ + assert hass.states.get('climate.mock_node_mock_value').attributes[ 'temperature'] == 22.0 - assert hass.states.get('climate.mock_node_mock_value_11_12_13').attributes[ + assert hass.states.get('climate.mock_node_mock_value').attributes[ 'current_temperature'] == 23.5 -@asyncio.coroutine -def test_scene_activated(hass, mock_openzwave): - """Test scene activated event.""" - mock_receivers = [] - - def mock_connect(receiver, signal, *args, **kwargs): - if signal == MockNetwork.SIGNAL_SCENE_EVENT: - mock_receivers.append(receiver) - - with patch('pydispatch.dispatcher.connect', new=mock_connect): - yield from async_setup_component(hass, 'zwave', {'zwave': {}}) - - assert len(mock_receivers) == 1 - - events = [] - - def listener(event): - events.append(event) - - hass.bus.async_listen(const.EVENT_SCENE_ACTIVATED, listener) - - node = MockNode(node_id=11) - scene_id = 123 - hass.async_add_job(mock_receivers[0], node, scene_id) - yield from hass.async_block_till_done() - - assert len(events) == 1 - assert events[0].data[ATTR_ENTITY_ID] == "mock_node_11" - assert events[0].data[const.ATTR_OBJECT_ID] == "mock_node_11" - assert events[0].data[const.ATTR_SCENE_ID] == scene_id - - -@asyncio.coroutine -def test_node_event_activated(hass, mock_openzwave): - """Test Node event activated event.""" - mock_receivers = [] - - def mock_connect(receiver, signal, *args, **kwargs): - if signal == MockNetwork.SIGNAL_NODE_EVENT: - mock_receivers.append(receiver) - - with patch('pydispatch.dispatcher.connect', new=mock_connect): - yield from async_setup_component(hass, 'zwave', {'zwave': {}}) - - assert len(mock_receivers) == 1 - - events = [] - - def listener(event): - events.append(event) - - hass.bus.async_listen(const.EVENT_NODE_EVENT, listener) - - node = MockNode(node_id=11) - value = 234 - hass.async_add_job(mock_receivers[0], node, value) - yield from hass.async_block_till_done() - - assert len(events) == 1 - assert events[0].data[const.ATTR_OBJECT_ID] == "mock_node_11" - assert events[0].data[const.ATTR_BASIC_LEVEL] == value - - @asyncio.coroutine def test_network_ready(hass, mock_openzwave): """Test Node network ready event.""" @@ -393,7 +337,9 @@ def test_network_ready(hass, mock_openzwave): mock_receivers.append(receiver) with patch('pydispatch.dispatcher.connect', new=mock_connect): - yield from async_setup_component(hass, 'zwave', {'zwave': {}}) + yield from async_setup_component(hass, 'zwave', {'zwave': { + 'new_entity_ids': True, + }}) assert len(mock_receivers) == 1 @@ -420,7 +366,9 @@ def test_network_complete(hass, mock_openzwave): mock_receivers.append(receiver) with patch('pydispatch.dispatcher.connect', new=mock_connect): - yield from async_setup_component(hass, 'zwave', {'zwave': {}}) + yield from async_setup_component(hass, 'zwave', {'zwave': { + 'new_entity_ids': True, + }}) assert len(mock_receivers) == 1 @@ -450,7 +398,9 @@ class TestZWaveDeviceEntityValues(unittest.TestCase): self.hass = get_test_home_assistant() self.hass.start() - setup_component(self.hass, 'zwave', {'zwave': {}}) + setup_component(self.hass, 'zwave', {'zwave': { + 'new_entity_ids': True, + }}) self.hass.block_till_done() self.node = MockNode() @@ -478,9 +428,10 @@ class TestZWaveDeviceEntityValues(unittest.TestCase): self.no_match_value = MockValue( command_class='mock_bad_class', node=self.node) - self.entity_id = '{}.{}'.format('mock_component', - zwave.object_id(self.primary)) - self.zwave_config = {} + self.entity_id = 'mock_component.mock_node_mock_value' + self.zwave_config = {'zwave': { + 'new_entity_ids': True, + }} self.device_config = {self.entity_id: {}} def tearDown(self): # pylint: disable=invalid-name @@ -491,6 +442,11 @@ class TestZWaveDeviceEntityValues(unittest.TestCase): @patch.object(zwave, 'discovery') def test_entity_discovery(self, discovery, get_platform): """Test the creation of a new entity.""" + mock_platform = MagicMock() + get_platform.return_value = mock_platform + mock_device = MagicMock() + mock_device.name = 'test_device' + mock_platform.get_device.return_value = mock_device values = zwave.ZWaveDeviceEntityValues( hass=self.hass, schema=self.mock_schema, @@ -550,6 +506,11 @@ class TestZWaveDeviceEntityValues(unittest.TestCase): @patch.object(zwave, 'discovery') def test_entity_existing_values(self, discovery, get_platform): """Test the loading of already discovered values.""" + mock_platform = MagicMock() + get_platform.return_value = mock_platform + mock_device = MagicMock() + mock_device.name = 'test_device' + mock_platform.get_device.return_value = mock_device self.node.values = { self.primary.value_id: self.primary, self.secondary.value_id: self.secondary, @@ -613,11 +574,15 @@ class TestZWaveDeviceEntityValues(unittest.TestCase): @patch.object(zwave, 'discovery') def test_entity_workaround_component(self, discovery, get_platform): """Test ignore workaround.""" + mock_platform = MagicMock() + get_platform.return_value = mock_platform + mock_device = MagicMock() + mock_device.name = 'test_device' + mock_platform.get_device.return_value = mock_device self.node.manufacturer_id = '010f' self.node.product_type = '0b00' self.primary.command_class = const.COMMAND_CLASS_SENSOR_ALARM - self.entity_id = '{}.{}'.format('binary_sensor', - zwave.object_id(self.primary)) + self.entity_id = 'binary_sensor.mock_node_mock_value' self.device_config = {self.entity_id: {}} self.mock_schema = { @@ -721,6 +686,11 @@ class TestZWaveDeviceEntityValues(unittest.TestCase): @patch.object(zwave, 'discovery') def test_config_polling_intensity(self, discovery, get_platform): """Test polling intensity.""" + mock_platform = MagicMock() + get_platform.return_value = mock_platform + mock_device = MagicMock() + mock_device.name = 'test_device' + mock_platform.get_device.return_value = mock_device self.node.values = { self.primary.value_id: self.primary, self.secondary.value_id: self.secondary, @@ -770,7 +740,9 @@ class TestZWaveServices(unittest.TestCase): self.hass.start() # Initialize zwave - setup_component(self.hass, 'zwave', {'zwave': {}}) + setup_component(self.hass, 'zwave', {'zwave': { + 'new_entity_ids': True, + }}) self.hass.block_till_done() self.zwave_network = self.hass.data[DATA_NETWORK] self.zwave_network.state = MockNetwork.STATE_READY diff --git a/tests/components/zwave/test_node_entity.py b/tests/components/zwave/test_node_entity.py index 73e8e163096..b7148dd982e 100644 --- a/tests/components/zwave/test_node_entity.py +++ b/tests/components/zwave/test_node_entity.py @@ -4,7 +4,8 @@ import unittest from unittest.mock import patch, MagicMock import tests.mock.zwave as mock_zwave import pytest -from homeassistant.components.zwave import node_entity +from homeassistant.components.zwave import node_entity, const +from homeassistant.const import ATTR_ENTITY_ID @asyncio.coroutine @@ -30,6 +31,92 @@ def test_maybe_schedule_update(hass, mock_openzwave): assert len(mock_call_later.mock_calls) == 2 +@asyncio.coroutine +def test_node_event_activated(hass, mock_openzwave): + """Test Node event activated event.""" + mock_receivers = [] + + def mock_connect(receiver, signal, *args, **kwargs): + if signal == mock_zwave.MockNetwork.SIGNAL_NODE_EVENT: + mock_receivers.append(receiver) + + node = mock_zwave.MockNode(node_id=11) + + with patch('pydispatch.dispatcher.connect', new=mock_connect): + entity = node_entity.ZWaveNodeEntity(node, mock_openzwave, True) + + assert len(mock_receivers) == 1 + + events = [] + + def listener(event): + events.append(event) + + hass.bus.async_listen(const.EVENT_NODE_EVENT, listener) + + # Test event before entity added to hass + value = 234 + hass.async_add_job(mock_receivers[0], node, value) + yield from hass.async_block_till_done() + assert len(events) == 0 + + # Add entity to hass + entity.hass = hass + entity.entity_id = 'zwave.mock_node' + + value = 234 + hass.async_add_job(mock_receivers[0], node, value) + yield from hass.async_block_till_done() + + assert len(events) == 1 + assert events[0].data[ATTR_ENTITY_ID] == "zwave.mock_node" + assert events[0].data[const.ATTR_NODE_ID] == 11 + assert events[0].data[const.ATTR_BASIC_LEVEL] == value + + +@asyncio.coroutine +def test_scene_activated(hass, mock_openzwave): + """Test scene activated event.""" + mock_receivers = [] + + def mock_connect(receiver, signal, *args, **kwargs): + if signal == mock_zwave.MockNetwork.SIGNAL_SCENE_EVENT: + mock_receivers.append(receiver) + + node = mock_zwave.MockNode(node_id=11) + + with patch('pydispatch.dispatcher.connect', new=mock_connect): + entity = node_entity.ZWaveNodeEntity(node, mock_openzwave, True) + + assert len(mock_receivers) == 1 + + events = [] + + def listener(event): + events.append(event) + + hass.bus.async_listen(const.EVENT_SCENE_ACTIVATED, listener) + + # Test event before entity added to hass + scene_id = 123 + hass.async_add_job(mock_receivers[0], node, scene_id) + yield from hass.async_block_till_done() + assert len(events) == 0 + + # Add entity to hass + entity.hass = hass + entity.entity_id = 'zwave.mock_node' + + scene_id = 123 + hass.async_add_job(mock_receivers[0], node, scene_id) + yield from hass.async_block_till_done() + + assert len(events) == 1 + assert events[0].data[ATTR_ENTITY_ID] == "zwave.mock_node" + assert events[0].data[const.ATTR_NODE_ID] == 11 + assert events[0].data[const.ATTR_SCENE_ID] == scene_id + + @pytest.mark.usefixtures('mock_openzwave') class TestZWaveNodeEntity(unittest.TestCase): """Class to test ZWaveNodeEntity.""" @@ -44,7 +131,7 @@ class TestZWaveNodeEntity(unittest.TestCase): self.node.manufacturer_name = 'Test Manufacturer' self.node.product_name = 'Test Product' self.entity = node_entity.ZWaveNodeEntity(self.node, - self.zwave_network) + self.zwave_network, True) def test_network_node_changed_from_value(self): """Test for network_node_changed.""" @@ -85,6 +172,8 @@ class TestZWaveNodeEntity(unittest.TestCase): {'node_id': self.node.node_id, 'node_name': 'Mock Node', 'manufacturer_name': 'Test Manufacturer', + 'old_entity_id': 'zwave.mock_node_567', + 'new_entity_id': 'zwave.mock_node', 'product_name': 'Test Product'}, self.entity.device_state_attributes) @@ -143,6 +232,8 @@ class TestZWaveNodeEntity(unittest.TestCase): {'node_id': self.node.node_id, 'node_name': 'Mock Node', 'manufacturer_name': 'Test Manufacturer', + 'old_entity_id': 'zwave.mock_node_567', + 'new_entity_id': 'zwave.mock_node', 'product_name': 'Test Product', 'query_stage': 'Dynamic', 'is_awake': True,