diff --git a/homeassistant/components/cover/mqtt.py b/homeassistant/components/cover/mqtt.py index d44d011bcb1..a53e659c448 100644 --- a/homeassistant/components/cover/mqtt.py +++ b/homeassistant/components/cover/mqtt.py @@ -14,7 +14,9 @@ import homeassistant.components.mqtt as mqtt from homeassistant.components.cover import ( CoverDevice, ATTR_TILT_POSITION, SUPPORT_OPEN_TILT, SUPPORT_CLOSE_TILT, SUPPORT_STOP_TILT, SUPPORT_SET_TILT_POSITION, - SUPPORT_OPEN, SUPPORT_CLOSE, SUPPORT_STOP, SUPPORT_SET_POSITION) + SUPPORT_OPEN, SUPPORT_CLOSE, SUPPORT_STOP, SUPPORT_SET_POSITION, + ATTR_POSITION) +from homeassistant.exceptions import TemplateError from homeassistant.const import ( CONF_NAME, CONF_VALUE_TEMPLATE, CONF_OPTIMISTIC, STATE_OPEN, STATE_CLOSED, STATE_UNKNOWN) @@ -29,6 +31,8 @@ DEPENDENCIES = ['mqtt'] CONF_TILT_COMMAND_TOPIC = 'tilt_command_topic' CONF_TILT_STATUS_TOPIC = 'tilt_status_topic' +CONF_POSITION_TOPIC = 'set_position_topic' +CONF_SET_POSITION_TEMPLATE = 'set_position_template' CONF_PAYLOAD_OPEN = 'payload_open' CONF_PAYLOAD_CLOSE = 'payload_close' @@ -55,10 +59,17 @@ DEFAULT_TILT_MAX = 100 DEFAULT_TILT_OPTIMISTIC = False DEFAULT_TILT_INVERT_STATE = False +OPEN_CLOSE_FEATURES = (SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP) TILT_FEATURES = (SUPPORT_OPEN_TILT | SUPPORT_CLOSE_TILT | SUPPORT_STOP_TILT | SUPPORT_SET_TILT_POSITION) -PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({ +PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_COMMAND_TOPIC, default=None): valid_publish_topic, + vol.Optional(CONF_POSITION_TOPIC, default=None): valid_publish_topic, + vol.Optional(CONF_SET_POSITION_TEMPLATE, default=None): cv.template, + vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, + vol.Optional(CONF_STATE_TOPIC): valid_subscribe_topic, + vol.Optional(CONF_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_PAYLOAD_OPEN, default=DEFAULT_PAYLOAD_OPEN): cv.string, vol.Optional(CONF_PAYLOAD_CLOSE, default=DEFAULT_PAYLOAD_CLOSE): cv.string, @@ -87,6 +98,9 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): value_template = config.get(CONF_VALUE_TEMPLATE) if value_template is not None: value_template.hass = hass + set_position_template = config.get(CONF_SET_POSITION_TEMPLATE) + if set_position_template is not None: + set_position_template.hass = hass async_add_devices([MqttCover( config.get(CONF_NAME), @@ -109,6 +123,8 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): config.get(CONF_TILT_MAX), config.get(CONF_TILT_STATE_OPTIMISTIC), config.get(CONF_TILT_INVERT_STATE), + config.get(CONF_POSITION_TOPIC), + set_position_template, )]) @@ -120,7 +136,7 @@ class MqttCover(CoverDevice): payload_open, payload_close, payload_stop, optimistic, value_template, tilt_open_position, tilt_closed_position, tilt_min, tilt_max, tilt_optimistic, - tilt_invert): + tilt_invert, position_topic, set_position_template): """Initialize the cover.""" self._position = None self._state = None @@ -145,6 +161,8 @@ class MqttCover(CoverDevice): self._tilt_max = tilt_max self._tilt_optimistic = tilt_optimistic self._tilt_invert = tilt_invert + self._position_topic = position_topic + self._set_position_template = set_position_template @asyncio.coroutine def async_added_to_hass(self): @@ -233,9 +251,11 @@ class MqttCover(CoverDevice): @property def supported_features(self): """Flag supported features.""" - supported_features = SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP + supported_features = 0 + if self._command_topic is not None: + supported_features = OPEN_CLOSE_FEATURES - if self.current_cover_position is not None: + if self._position_topic is not None: supported_features |= SUPPORT_SET_POSITION if self._tilt_command_topic is not None: @@ -315,6 +335,22 @@ class MqttCover(CoverDevice): mqtt.async_publish(self.hass, self._tilt_command_topic, level, self._qos, self._retain) + @asyncio.coroutine + def async_set_cover_position(self, **kwargs): + """Move the cover to a specific position.""" + if ATTR_POSITION in kwargs: + position = kwargs[ATTR_POSITION] + if self._set_position_template is not None: + try: + position = self._set_position_template.async_render( + **kwargs) + except TemplateError as ex: + _LOGGER.error(ex) + self._state = None + + mqtt.async_publish(self.hass, self._position_topic, + position, self._qos, self._retain) + def find_percentage_in_range(self, position): """Find the 0-100% value within the specified range.""" # the range of motion as defined by the min max values diff --git a/homeassistant/components/media_player/openhome.py b/homeassistant/components/media_player/openhome.py index 25d6390bf08..b2242bfecad 100644 --- a/homeassistant/components/media_player/openhome.py +++ b/homeassistant/components/media_player/openhome.py @@ -14,7 +14,7 @@ from homeassistant.components.media_player import ( from homeassistant.const import ( STATE_IDLE, STATE_PAUSED, STATE_PLAYING, STATE_OFF) -REQUIREMENTS = ['openhomedevice==0.4.0'] +REQUIREMENTS = ['openhomedevice==0.4.2'] SUPPORT_OPENHOME = SUPPORT_SELECT_SOURCE | \ SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_MUTE | SUPPORT_VOLUME_SET | \ @@ -60,7 +60,6 @@ class OpenhomeDevice(MediaPlayerDevice): self._track_information = {} self._in_standby = None self._transport_state = None - self._track_information = None self._volume_level = None self._volume_muted = None self._supported_features = SUPPORT_OPENHOME @@ -173,27 +172,29 @@ class OpenhomeDevice(MediaPlayerDevice): @property def media_image_url(self): """Image url of current playing media.""" - return self._track_information["albumArtwork"] + return self._track_information.get('albumArtwork') @property def media_artist(self): """Artist of current playing media, music track only.""" - return self._track_information["artist"][0] + artists = self._track_information.get('artist') + if artists: + return artists[0] @property def media_album_name(self): """Album name of current playing media, music track only.""" - return self._track_information["albumTitle"] + return self._track_information.get('albumTitle') @property def media_title(self): """Title of current playing media.""" - return self._track_information["title"] + return self._track_information.get('title') @property def source(self): """Name of the current input source.""" - return self._source["name"] + return self._source.get('name') @property def volume_level(self): diff --git a/homeassistant/components/media_player/roku.py b/homeassistant/components/media_player/roku.py index a81f9330ab8..aac6b1a228d 100644 --- a/homeassistant/components/media_player/roku.py +++ b/homeassistant/components/media_player/roku.py @@ -125,8 +125,7 @@ class RokuDevice(MediaPlayerDevice): """Return the name of the device.""" if self.device_info.userdevicename: return self.device_info.userdevicename - else: - return "roku_" + self.roku.device_info.sernum + return "Roku {}".format(self.device_info.sernum) @property def state(self): @@ -158,8 +157,7 @@ class RokuDevice(MediaPlayerDevice): return None elif self.current_app.name == "Roku": return None - else: - return MEDIA_TYPE_VIDEO + return MEDIA_TYPE_VIDEO @property def media_image_url(self): diff --git a/homeassistant/components/sensor/dsmr.py b/homeassistant/components/sensor/dsmr.py index 8df4776459e..23324fe7360 100644 --- a/homeassistant/components/sensor/dsmr.py +++ b/homeassistant/components/sensor/dsmr.py @@ -40,8 +40,7 @@ import voluptuous as vol _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['dsmr_parser==0.9'] - +REQUIREMENTS = ['dsmr_parser==0.8'] CONF_DSMR_VERSION = 'dsmr_version' CONF_RECONNECT_INTERVAL = 'reconnect_interval' @@ -61,7 +60,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.string, vol.Optional(CONF_HOST, default=None): cv.string, vol.Optional(CONF_DSMR_VERSION, default=DEFAULT_DSMR_VERSION): vol.All( - cv.string, vol.In(['5', '4', '2.2'])), + cv.string, vol.In(['4', '2.2'])), vol.Optional(CONF_RECONNECT_INTERVAL, default=30): int, }) @@ -94,7 +93,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): devices = [DSMREntity(name, obis) for name, obis in obis_mapping] # Protocol version specific obis - if dsmr_version in ('4', '5'): + if dsmr_version == '4': gas_obis = obis_ref.HOURLY_GAS_METER_READING else: gas_obis = obis_ref.GAS_METER_READING diff --git a/homeassistant/components/sensor/metoffice.py b/homeassistant/components/sensor/metoffice.py index c1ffb01d212..961b4692e93 100644 --- a/homeassistant/components/sensor/metoffice.py +++ b/homeassistant/components/sensor/metoffice.py @@ -141,7 +141,7 @@ class MetOfficeCurrentSensor(Entity): attr['Sensor Id'] = self._condition attr['Site Id'] = self.site.id attr['Site Name'] = self.site.name - attr['Last Update'] = self.data.lastupdate + attr['Last Update'] = self.data.data.date attr[ATTR_ATTRIBUTION] = CONF_ATTRIBUTION return attr diff --git a/homeassistant/components/sensor/wunderground.py b/homeassistant/components/sensor/wunderground.py index 4d684f405f8..67c96e66062 100644 --- a/homeassistant/components/sensor/wunderground.py +++ b/homeassistant/components/sensor/wunderground.py @@ -574,16 +574,16 @@ SENSOR_TYPES = { "Precipitation Intensity in 4 Days", 3, 'qpf_allday', 'in', LENGTH_INCHES, "mdi:umbrella"), 'precip_1d': WUDailySimpleForecastSensorConfig( - "Percipitation Probability Today", 0, "pop", None, "%", + "Precipitation Probability Today", 0, "pop", None, "%", "mdi:umbrella"), 'precip_2d': WUDailySimpleForecastSensorConfig( - "Percipitation Probability Tomorrow", 1, "pop", None, "%", + "Precipitation Probability Tomorrow", 1, "pop", None, "%", "mdi:umbrella"), 'precip_3d': WUDailySimpleForecastSensorConfig( - "Percipitation Probability in 3 Days", 2, "pop", None, "%", + "Precipitation Probability in 3 Days", 2, "pop", None, "%", "mdi:umbrella"), 'precip_4d': WUDailySimpleForecastSensorConfig( - "Percipitation Probability in 4 Days", 3, "pop", None, "%", + "Precipitation Probability in 4 Days", 3, "pop", None, "%", "mdi:umbrella"), } diff --git a/homeassistant/components/zwave/__init__.py b/homeassistant/components/zwave/__init__.py index 640db9d4939..f3507211907 100755 --- a/homeassistant/components/zwave/__init__.py +++ b/homeassistant/components/zwave/__init__.py @@ -30,7 +30,7 @@ from homeassistant.components.frontend import register_built_in_panel from . import api from . import const -from .const import DOMAIN, DATA_DEVICES, DATA_NETWORK +from .const import DOMAIN, DATA_DEVICES, DATA_NETWORK, DATA_ENTITY_VALUES from .node_entity import ZWaveBaseEntity, ZWaveNodeEntity from . import workaround from .discovery_schemas import DISCOVERY_SCHEMAS @@ -74,12 +74,20 @@ RENAME_NODE_SCHEMA = vol.Schema({ vol.Required(const.ATTR_NODE_ID): vol.Coerce(int), vol.Required(const.ATTR_NAME): cv.string, }) + +RENAME_VALUE_SCHEMA = vol.Schema({ + vol.Required(const.ATTR_NODE_ID): vol.Coerce(int), + vol.Required(const.ATTR_VALUE_ID): vol.Coerce(int), + vol.Required(const.ATTR_NAME): cv.string, +}) + SET_CONFIG_PARAMETER_SCHEMA = vol.Schema({ vol.Required(const.ATTR_NODE_ID): vol.Coerce(int), vol.Required(const.ATTR_CONFIG_PARAMETER): vol.Coerce(int), vol.Required(const.ATTR_CONFIG_VALUE): vol.Any(vol.Coerce(int), cv.string), vol.Optional(const.ATTR_CONFIG_SIZE, default=2): vol.Coerce(int) }) + PRINT_CONFIG_PARAMETER_SCHEMA = vol.Schema({ vol.Required(const.ATTR_NODE_ID): vol.Coerce(int), vol.Required(const.ATTR_CONFIG_PARAMETER): vol.Coerce(int), @@ -258,6 +266,7 @@ def setup(hass, config): network = hass.data[DATA_NETWORK] = ZWaveNetwork(options, autostart=False) hass.data[DATA_DEVICES] = {} + hass.data[DATA_ENTITY_VALUES] = [] if use_debug: # pragma: no cover def log_all(signal, value=None): @@ -276,12 +285,10 @@ def setup(hass, config): dispatcher.connect(log_all, weak=False) - discovered_values = [] - def value_added(node, value): """Handle new added value to a node on the network.""" # Check if this value should be tracked by an existing entity - for values in discovered_values: + for values in hass.data[DATA_ENTITY_VALUES]: values.check_value(value) for schema in DISCOVERY_SCHEMAS: @@ -294,7 +301,11 @@ def setup(hass, config): values = ZWaveDeviceEntityValues( hass, schema, value, config, device_config) - discovered_values.append(values) + + # We create a new list and update the reference here so that + # the list can be safely iterated over in the main thread + new_values = hass.data[DATA_ENTITY_VALUES] + [values] + hass.data[DATA_ENTITY_VALUES] = new_values component = EntityComponent(_LOGGER, DOMAIN, hass) @@ -401,6 +412,18 @@ def setup(hass, config): _LOGGER.info( "Renamed Z-Wave node %d to %s", node_id, name) + def rename_value(service): + """Rename a node value.""" + node_id = service.data.get(const.ATTR_NODE_ID) + value_id = service.data.get(const.ATTR_VALUE_ID) + node = network.nodes[node_id] + value = node.values[value_id] + name = service.data.get(const.ATTR_NAME) + value.label = name + _LOGGER.info( + "Renamed Z-Wave value (Node %d Value %d) to %s", + node_id, value_id, name) + def remove_failed_node(service): """Remove failed node.""" node_id = service.data.get(const.ATTR_NODE_ID) @@ -585,6 +608,10 @@ def setup(hass, config): hass.services.register(DOMAIN, const.SERVICE_RENAME_NODE, rename_node, descriptions[const.SERVICE_RENAME_NODE], schema=RENAME_NODE_SCHEMA) + hass.services.register(DOMAIN, const.SERVICE_RENAME_VALUE, + rename_value, + descriptions[const.SERVICE_RENAME_VALUE], + schema=RENAME_VALUE_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_SET_CONFIG_PARAMETER, set_config_parameter, descriptions[ @@ -644,6 +671,7 @@ def setup(hass, config): if 'frontend' in hass.config.components: register_built_in_panel(hass, 'zwave', 'Z-Wave', 'mdi:nfc') + hass.http.register_view(api.ZWaveNodeValueView) hass.http.register_view(api.ZWaveNodeGroupView) hass.http.register_view(api.ZWaveNodeConfigView) hass.http.register_view(api.ZWaveUserCodeView) diff --git a/homeassistant/components/zwave/api.py b/homeassistant/components/zwave/api.py index 1cf3b38b26f..85e7b9c0f8f 100644 --- a/homeassistant/components/zwave/api.py +++ b/homeassistant/components/zwave/api.py @@ -9,6 +9,32 @@ from . import const _LOGGER = logging.getLogger(__name__) +class ZWaveNodeValueView(HomeAssistantView): + """View to return the node values.""" + + url = r"/api/zwave/values/{node_id:\d+}" + name = "api:zwave:values" + + @ha.callback + def get(self, request, node_id): + """Retrieve groups of node.""" + nodeid = int(node_id) + hass = request.app['hass'] + values_list = hass.data[const.DATA_ENTITY_VALUES] + + values_data = {} + # Return a list of values for this node that are used as a + # primary value for an entity + for entity_values in values_list: + if entity_values.primary.node.node_id != nodeid: + continue + + values_data[entity_values.primary.value_id] = { + 'label': entity_values.primary.label, + } + return self.json(values_data) + + class ZWaveNodeGroupView(HomeAssistantView): """View to return the nodes group configuration.""" diff --git a/homeassistant/components/zwave/const.py b/homeassistant/components/zwave/const.py index dd88dccaa80..6f8c7d1fe75 100644 --- a/homeassistant/components/zwave/const.py +++ b/homeassistant/components/zwave/const.py @@ -20,6 +20,7 @@ DISCOVERY_DEVICE = 'device' DATA_DEVICES = 'zwave_devices' DATA_NETWORK = 'zwave_network' +DATA_ENTITY_VALUES = 'zwave_entity_values' SERVICE_CHANGE_ASSOCIATION = "change_association" SERVICE_ADD_NODE = "add_node" @@ -38,6 +39,7 @@ SERVICE_SET_WAKEUP = "set_wakeup" SERVICE_STOP_NETWORK = "stop_network" SERVICE_START_NETWORK = "start_network" SERVICE_RENAME_NODE = "rename_node" +SERVICE_RENAME_VALUE = "rename_value" SERVICE_REFRESH_ENTITY = "refresh_entity" SERVICE_REFRESH_NODE = "refresh_node" SERVICE_RESET_NODE_METERS = "reset_node_meters" diff --git a/homeassistant/components/zwave/services.yaml b/homeassistant/components/zwave/services.yaml index 6f61dec5cc7..4a7379fbf56 100644 --- a/homeassistant/components/zwave/services.yaml +++ b/homeassistant/components/zwave/services.yaml @@ -109,6 +109,19 @@ rename_node: description: New Name example: 'kitchen' +rename_value: + description: Set the name of a node value. Value IDs can be queried from /api/zwave/values/{node_id} + fields: + node_id: + description: ID of the node to rename. + example: 10 + value_id: + description: ID of the value to rename. + example: 72037594255792737 + name: + description: New Name + example: 'Luminosity' + reset_node_meters: description: Resets the meter counters of a node. fields: diff --git a/homeassistant/const.py b/homeassistant/const.py index 3dcbd9c586c..9fab663f964 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 46 -PATCH_VERSION = '0' +PATCH_VERSION = '1' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 4, 2) diff --git a/requirements_all.txt b/requirements_all.txt index dee3c92ee4e..8470a78f8aa 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -162,7 +162,7 @@ dnspython3==1.15.0 dovado==0.4.1 # homeassistant.components.sensor.dsmr -dsmr_parser==0.9 +dsmr_parser==0.8 # homeassistant.components.dweet # homeassistant.components.sensor.dweet @@ -406,7 +406,7 @@ onkyo-eiscp==1.1 openevsewifi==0.4 # homeassistant.components.media_player.openhome -openhomedevice==0.4.0 +openhomedevice==0.4.2 # homeassistant.components.switch.orvibo orvibo==1.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f56ab8c3194..ded0c661226 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -37,7 +37,7 @@ aiohttp_cors==0.5.3 apns2==0.1.1 # homeassistant.components.sensor.dsmr -dsmr_parser==0.9 +dsmr_parser==0.8 # homeassistant.components.climate.honeywell evohomeclient==0.2.5 diff --git a/tests/components/cover/test_mqtt.py b/tests/components/cover/test_mqtt.py index e685a51f56c..8b6202acdff 100644 --- a/tests/components/cover/test_mqtt.py +++ b/tests/components/cover/test_mqtt.py @@ -215,6 +215,8 @@ class TestCoverMQTT(unittest.TestCase): 'cover.test').attributes self.assertFalse('current_position' in state_attributes_dict) self.assertFalse('current_tilt_position' in state_attributes_dict) + self.assertFalse(4 & self.hass.states.get( + 'cover.test').attributes['supported_features'] == 4) fire_mqtt_message(self.hass, 'state-topic', '0') self.hass.block_till_done() @@ -240,6 +242,119 @@ class TestCoverMQTT(unittest.TestCase): 'cover.test').attributes['current_position'] self.assertEqual(50, current_cover_position) + def test_set_cover_position(self): + """Test setting cover position.""" + self.assertTrue(setup_component(self.hass, cover.DOMAIN, { + cover.DOMAIN: { + 'platform': 'mqtt', + 'name': 'test', + 'state_topic': 'state-topic', + 'command_topic': 'command-topic', + 'set_position_topic': 'position-topic', + 'payload_open': 'OPEN', + 'payload_close': 'CLOSE', + 'payload_stop': 'STOP' + } + })) + + state_attributes_dict = self.hass.states.get( + 'cover.test').attributes + self.assertFalse('current_position' in state_attributes_dict) + self.assertFalse('current_tilt_position' in state_attributes_dict) + + self.assertTrue(4 & self.hass.states.get( + 'cover.test').attributes['supported_features'] == 4) + + fire_mqtt_message(self.hass, 'state-topic', '22') + self.hass.block_till_done() + state_attributes_dict = self.hass.states.get( + 'cover.test').attributes + self.assertTrue('current_position' in state_attributes_dict) + self.assertFalse('current_tilt_position' in state_attributes_dict) + current_cover_position = self.hass.states.get( + 'cover.test').attributes['current_position'] + self.assertEqual(22, current_cover_position) + + def test_set_position_templated(self): + """Test setting cover position via template.""" + self.assertTrue(setup_component(self.hass, cover.DOMAIN, { + cover.DOMAIN: { + 'platform': 'mqtt', + 'name': 'test', + 'state_topic': 'state-topic', + 'command_topic': 'command-topic', + 'set_position_topic': 'position-topic', + 'set_position_template': '{{100-62}}', + 'payload_open': 'OPEN', + 'payload_close': 'CLOSE', + 'payload_stop': 'STOP' + } + })) + + cover.set_cover_position(self.hass, 100, 'cover.test') + self.hass.block_till_done() + + self.assertEqual(('position-topic', '38', 0, False), + self.mock_publish.mock_calls[-2][1]) + + def test_set_position_untemplated(self): + """Test setting cover position via template.""" + self.assertTrue(setup_component(self.hass, cover.DOMAIN, { + cover.DOMAIN: { + 'platform': 'mqtt', + 'name': 'test', + 'state_topic': 'state-topic', + 'command_topic': 'command-topic', + 'set_position_topic': 'position-topic', + 'payload_open': 'OPEN', + 'payload_close': 'CLOSE', + 'payload_stop': 'STOP' + } + })) + + cover.set_cover_position(self.hass, 62, 'cover.test') + self.hass.block_till_done() + + self.assertEqual(('position-topic', 62, 0, False), + self.mock_publish.mock_calls[-2][1]) + + def test_no_command_topic(self): + """Test with no command topic.""" + self.assertTrue(setup_component(self.hass, cover.DOMAIN, { + cover.DOMAIN: { + 'platform': 'mqtt', + 'name': 'test', + 'qos': 0, + 'payload_open': 'OPEN', + 'payload_close': 'CLOSE', + 'payload_stop': 'STOP', + 'tilt_command_topic': 'tilt-command', + 'tilt_status_topic': 'tilt-status' + } + })) + + self.assertEqual(240, self.hass.states.get( + 'cover.test').attributes['supported_features']) + + def test_with_command_topic_and_tilt(self): + """Test with command topic and tilt config.""" + self.assertTrue(setup_component(self.hass, cover.DOMAIN, { + cover.DOMAIN: { + 'command_topic': 'test', + 'platform': 'mqtt', + 'name': 'test', + 'qos': 0, + 'payload_open': 'OPEN', + 'payload_close': 'CLOSE', + 'payload_stop': 'STOP', + 'tilt_command_topic': 'tilt-command', + 'tilt_status_topic': 'tilt-status' + } + })) + + self.assertEqual(251, self.hass.states.get( + 'cover.test').attributes['supported_features']) + def test_tilt_defaults(self): """Test the defaults.""" self.assertTrue(setup_component(self.hass, cover.DOMAIN, { @@ -457,7 +572,7 @@ class TestCoverMQTT(unittest.TestCase): mqtt_cover = MqttCover( 'cover.test', 'foo', 'bar', 'fooBar', "fooBarBaz", 0, False, 'OPEN', 'CLOSE', 'OPEN', 'CLOSE', 'STOP', False, None, - 100, 0, 0, 100, False, False) + 100, 0, 0, 100, False, False, None, None) self.assertEqual(44, mqtt_cover.find_percentage_in_range(44)) @@ -466,7 +581,7 @@ class TestCoverMQTT(unittest.TestCase): mqtt_cover = MqttCover( 'cover.test', 'foo', 'bar', 'fooBar', "fooBarBaz", 0, False, 'OPEN', 'CLOSE', 'OPEN', 'CLOSE', 'STOP', False, None, - 180, 80, 80, 180, False, False) + 180, 80, 80, 180, False, False, None, None) self.assertEqual(40, mqtt_cover.find_percentage_in_range(120)) @@ -475,7 +590,7 @@ class TestCoverMQTT(unittest.TestCase): mqtt_cover = MqttCover( 'cover.test', 'foo', 'bar', 'fooBar', "fooBarBaz", 0, False, 'OPEN', 'CLOSE', 'OPEN', 'CLOSE', 'STOP', False, None, - 100, 0, 0, 100, False, True) + 100, 0, 0, 100, False, True, None, None) self.assertEqual(56, mqtt_cover.find_percentage_in_range(44)) @@ -484,7 +599,7 @@ class TestCoverMQTT(unittest.TestCase): mqtt_cover = MqttCover( 'cover.test', 'foo', 'bar', 'fooBar', "fooBarBaz", 0, False, 'OPEN', 'CLOSE', 'OPEN', 'CLOSE', 'STOP', False, None, - 180, 80, 80, 180, False, True) + 180, 80, 80, 180, False, True, None, None) self.assertEqual(60, mqtt_cover.find_percentage_in_range(120)) @@ -493,7 +608,7 @@ class TestCoverMQTT(unittest.TestCase): mqtt_cover = MqttCover( 'cover.test', 'foo', 'bar', 'fooBar', "fooBarBaz", 0, False, 'OPEN', 'CLOSE', 'OPEN', 'CLOSE', 'STOP', False, None, - 100, 0, 0, 100, False, False) + 100, 0, 0, 100, False, False, None, None) self.assertEqual(44, mqtt_cover.find_in_range_from_percent(44)) @@ -502,7 +617,7 @@ class TestCoverMQTT(unittest.TestCase): mqtt_cover = MqttCover( 'cover.test', 'foo', 'bar', 'fooBar', "fooBarBaz", 0, False, 'OPEN', 'CLOSE', 'OPEN', 'CLOSE', 'STOP', False, None, - 180, 80, 80, 180, False, False) + 180, 80, 80, 180, False, False, None, None) self.assertEqual(120, mqtt_cover.find_in_range_from_percent(40)) @@ -511,7 +626,7 @@ class TestCoverMQTT(unittest.TestCase): mqtt_cover = MqttCover( 'cover.test', 'foo', 'bar', 'fooBar', "fooBarBaz", 0, False, 'OPEN', 'CLOSE', 'OPEN', 'CLOSE', 'STOP', False, None, - 100, 0, 0, 100, False, True) + 100, 0, 0, 100, False, True, None, None) self.assertEqual(44, mqtt_cover.find_in_range_from_percent(56)) @@ -520,6 +635,6 @@ class TestCoverMQTT(unittest.TestCase): mqtt_cover = MqttCover( 'cover.test', 'foo', 'bar', 'fooBar', "fooBarBaz", 0, False, 'OPEN', 'CLOSE', 'OPEN', 'CLOSE', 'STOP', False, None, - 180, 80, 80, 180, False, True) + 180, 80, 80, 180, False, True, None, None) self.assertEqual(120, mqtt_cover.find_in_range_from_percent(60)) diff --git a/tests/components/zwave/test_api.py b/tests/components/zwave/test_api.py index c1b2022c5e1..cf597f4104c 100644 --- a/tests/components/zwave/test_api.py +++ b/tests/components/zwave/test_api.py @@ -3,9 +3,38 @@ import asyncio from unittest.mock import MagicMock from homeassistant.components.zwave import DATA_NETWORK, const from homeassistant.components.zwave.api import ( - ZWaveNodeGroupView, ZWaveNodeConfigView, ZWaveUserCodeView) + ZWaveNodeValueView, ZWaveNodeGroupView, ZWaveNodeConfigView, + ZWaveUserCodeView) from tests.common import mock_http_component_app -from tests.mock.zwave import MockNode, MockValue +from tests.mock.zwave import MockNode, MockValue, MockEntityValues + + +@asyncio.coroutine +def test_get_values(hass, test_client): + """Test getting values on node.""" + app = mock_http_component_app(hass) + ZWaveNodeValueView().register(app.router) + + node = MockNode(node_id=1) + value = MockValue(value_id=123456, node=node, label='Test Label') + values = MockEntityValues(primary=value) + node2 = MockNode(node_id=2) + value2 = MockValue(value_id=234567, node=node2, label='Test Label 2') + values2 = MockEntityValues(primary=value2) + hass.data[const.DATA_ENTITY_VALUES] = [values, values2] + + client = yield from test_client(app) + + resp = yield from client.get('/api/zwave/values/1') + + assert resp.status == 200 + result = yield from resp.json() + + assert result == { + '123456': { + 'label': 'Test Label', + } + } @asyncio.coroutine diff --git a/tests/components/zwave/test_init.py b/tests/components/zwave/test_init.py index 49ec1ea6a95..8b539d3a7c9 100644 --- a/tests/components/zwave/test_init.py +++ b/tests/components/zwave/test_init.py @@ -868,6 +868,23 @@ class TestZWaveServices(unittest.TestCase): assert self.zwave_network.nodes[11].name == 'test_name' + def test_rename_value(self): + """Test zwave rename_value service.""" + node = MockNode(node_id=14) + value = MockValue(index=12, value_id=123456, label="Old Label") + node.values = {123456: value} + self.zwave_network.nodes = {11: node} + + assert value.label == "Old Label" + self.hass.services.call('zwave', 'rename_value', { + const.ATTR_NODE_ID: 11, + const.ATTR_VALUE_ID: 123456, + const.ATTR_NAME: "New Label", + }) + self.hass.block_till_done() + + assert value.label == "New Label" + def test_remove_failed_node(self): """Test zwave remove_failed_node service.""" self.hass.services.call('zwave', 'remove_failed_node', {